new
操作符
new
操作符做了这些事:
- 它创建了一个全新的对象。
- 它会被执行
[[Prototype]]
(也就是__proto__
)链接。 - 它使
this
指向新创建的对象。。 - 通过
new
创建的每个对象将最终被[[Prototype]]
链接到这个函数的prototype
对象上。 - 如果函数没有返回对象类型
Object
(包含Functoin, Array, Date, RegExg, Error
),那么new
表达式中的函数调用将返回该对象引用。
function new(fn){
let res = {};
// 所有的引用类型(数组、对象、函数),`__ proto __ 属性值指向它的构造函数的" __ prototype __ "属性值`
if(fn.prototype !== null){
res.__proto__ = fn.prototype;
}
let ret = fn.apply(res,Array.prototype.call(arguments,1));
if((typeof ret === "object" || typeof ret === "function") && ret !== null)
{
return ret;
}
return res;
}
let obj = new(A,1,2);
//等价于
let obj = new A(1,2);
实现一个 instanceOf
function instanceOf(left,right) {
let proto = left.__proto__;
let prototype = right.prototype
while(true) {
if(proto === null) return false
if(proto === prototype) return true
proto = proto.__proto__;
}
}
数组去重
//去掉数组中重复性的数据
function trimSameItem(arr){
var newArr = [];
for (var i = 0; i < arr.length; i++) {
//方法一
// if(newArr.indexOf(arr[i]) === -1){
// newArr.push(arr[i]);
// }
//方法二
if(!newArr.includes(arr[i])){
newArr.push(arr[i]);
}
}
return newArr;
}
var arr = [8,11,20,5,20,8,0,2,4,0,8,19];
console.log(trimSameItem(arr));
// 方法 三
function unique(arr) {
var res = arr.filter(function(item, index, array) {
return array.indexOf(item) === index
})
return res
}
数组扁平化
数组扁平化就是将 [1, [2, [3]]] 这种多层的数组拍平成一层 [1, 2, 3]。使用 Array.prototype.flat 可以直接将多层数组拍平成一层:
[1, [2, [3]]].flat(2) // [1, 2, 3]
现在就是要实现 flat 这种效果。
// ES5 实现:递归
function flatten(arr) {
var result = [];
for (var i = 0, len = arr.length; i < len; i++) {
if (Array.isArray(arr[i])) {
result = result.concat(flatten(arr[i]))
} else {
result.push(arr[i])
}
}
return result;
}
// ES6 实现
function flatten(arr) {
while (arr.some(item => Array.isArray(item))) {
arr = [].concat(...arr);
}
return arr;
}
反转数组
//反转数组
function turnArray(arr) {
for (var i = 0; i < arr.length/2; i++) {
var temp = arr[i];
arr[i] = arr[arr.length-1-i];
arr[arr.length-1-i] = temp;
}
return arr;
}
var arr = [1,23,7,3,4,5,6,7,8,9];
console.log(turnArray(arr));
转换字符串为驼峰命名
//转换字符串为驼峰命名
function toTuoFeng(params) {
if(!params){
console.log('参数不存在');
return;
}
var arr = foo.split('-');
// console.log(arr[1].charAt(0).toUpperCase()+arr[1].substr(1,arr[1].length-1));
for (var i = 1; i < arr.length; i++) {
arr[i] = arr[i].charAt(0).toUpperCase()+arr[i].substr(1,arr[i].length-1);
}
return arr.join('');
}
var foo = 'get-element-by-id';
var zhuan = toTuoFeng(foo);
console.log(zhuan);
冒泡排序
//冒泡排序
var arr = [5,4,9,23,7,2,1];
//论数 arr.length-1
for (var i = 0; i < arr.length-1; i++) {
console.log(i);
for (var j = 0; j < arr.length-1-i; j++) {
//j>j+1二者对调位置
if(arr[j]>arr[j+1]){
var temp = arr[j+1];
arr[j+1] = arr[j];
arr[j] = temp;
}
//倒序
// if(arr[j]<arr[j+1]){
// temp = arr[j+1];
// arr[j+1] = arr[j];
// arr[j] = temp;
// }
}
}
console.log(arr);
实现 Call
call
语法:
fun.call(thisArg, arg1, arg2, ...)
,调用一个函数, 其具有一个指定的this值和分别地提供的参数(参数的列表)。
call
核心:
- 将函数设为对象的属性
- 执行&删除这个函数
- 指定
this
到函数并传入给定参数执行函数 - 如果不传入参数,默认指向为 window
- this 可能传入 null;
- 传入不固定个数的参数;
- 函数可能有返回值;
Function.prototype.call2 = function(content = window) {
content.fn = this;
let args = [...arguments].slice(1);
let result = content.fn(...args);
delete content.fn;
return result;
}
let foo = {
value: 1
}
function bar(name, age) {
console.log(name)
console.log(age)
console.log(this.value);
}
bar.call2(foo, 'black', '18') // black 18 1
apply
的模拟实现
apply()
的实现和call()
类似,只是参数形式不同。
Function.prototype.apply2 = function(context = window) {
context.fn = this
let result;
// 判断是否有第二个参数
if(arguments[1]) {
result = context.fn(...arguments[1])
} else {
result = context.fn()
}
delete context.fn
return result
}
bind()
的模拟实现
Function.prototype.bind2 = function(content) {
if(typeof this != "function") {
throw Error("not a function")
}
// 若没问参数类型则从这开始写
let fn = this;
let args = [...arguments].slice(1);
let resFn = function() {
return fn.apply(this instanceof resFn ? this : content,args.concat(...arguments) )
}
function tmp() {}
tmp.prototype = this.prototype;
resFn.prototype = new tmp();
return resFn;
}
实现一个JS函数柯里化
在计算机科学中,柯里化(Currying)是把接受多个参数的函数变换成接受一个单一参数(最初函数的第一个参数)的函数,并且返回接受余下的参数且返回结果的新函数的技术。
function curry(fn, args) {
var length = fn.length;
var args = args || [];
return function(){
newArgs = args.concat(Array.prototype.slice.call(arguments));
if (newArgs.length < length) {
return curry.call(this,fn,newArgs);
}else{
return fn.apply(this,newArgs);
}
}
}
function multiFn(a, b, c) {
return a * b * c;
}
var multi = curry(multiFn);
multi(2)(3)(4);
multi(2,3,4);
multi(2)(3,4);
multi(2,3)(4);
// ES6 版本
const curry = (fn, arr = []) => (...args) => (
arg => arg.length === fn.length
? fn(...arg)
: curry(fn, arg)
)([...arr, ...args])
let curryTest=curry((a,b,c,d)=>a+b+c+d)
curryTest(1,2,3)(4) //返回10
curryTest(1,2)(4)(3) //返回10
curryTest(1,2)(3,4) //返回10
手写一个Promise
我们来过一遍Promise/A+
规范:
- 三种状态
pending| fulfilled(resolved) | rejected
- 当处于
pending
状态的时候,可以转移到fulfilled(resolved)
或者rejected
状态 - 当处于
fulfilled(resolved)
状态或者rejected
状态的时候,就不可变。
- 必须有一个
then
异步执行方法,then
接受两个参数且必须返回一个promise:
var promise = new Promise((resolve,reject) => {
if (操作成功) {
resolve(value)
} else {
reject(error)
}
})
promise.then(function (value) {
// success
},function (value) {
// failure
})
节流(Throttling
)实现
触发高频事件,且 N 秒内只执行一次。
function throttle(fn, wait) {
let prev = new Date();
return function() {
const args = arguments;
const now = new Date();
if (now - prev > wait) {
fn.apply(this, args);
prev = new Date();
}
}
防抖函数 有问题
触发高频事件 N 秒后只会执行一次,如果 N 秒内事件再次触发,则会重新计时。
// 防抖动函数
function debounce(func, wait=50, immediate) {
var timeout;
return function() {
var context = this, args = arguments;
var later = function() {
timeout = null;
if (!immediate) func.apply(context, args);
};
var callNow = immediate && !timeout;
clearTimeout(timeout);
timeout = setTimeout(later, wait);
if (callNow) func.apply(context, args);
};
};
深拷贝 有问题
function deepClone(obj) {
if (typeof obj !== 'object') return;
var newObj = obj instanceof Array ? [] : {};
for (var key in obj) {
if (obj.hasOwnProperty(key)) {
newObj[key] = typeof obj[key] === 'object' ? deepClone(obj[key]) : obj[key];
}
}
return newObj;
}
function deepCopy(obj){//有问题
//判断是否是简单数据类型,
if(typeof obj == "object"){
//复杂数据类型
var result = obj.constructor == Array ? [] : {};
for(let i in obj){
result[i] = typeof obj[i] == "object" ? deepCopy(obj[i]) : obj[i];
}
}else {
//简单数据类型 直接 == 赋值
var result = obj;
}
return result;
}
解析 URL 参数为对象
function parseParam(url) {
const paramsStr = /.+\?(.+)$/.exec(url)[1]; // 将 ? 后面的字符串取出来
const paramsArr = paramsStr.split('&'); // 将字符串以 & 分割后存到数组中
let paramsObj = {};
// 将 params 存到对象中
paramsArr.forEach(param => {
if (/=/.test(param)) { // 处理有 value 的参数
let [key, val] = param.split('='); // 分割 key 和 value
val = decodeURIComponent(val); // 解码
val = /^\d+$/.test(val) ? parseFloat(val) : val; // 判断是否转为数字
if (paramsObj.hasOwnProperty(key)) { // 如果对象有 key,则添加一个值
paramsObj[key] = [].concat(paramsObj[key], val);
} else { // 如果对象没有这个 key,创建 key 并设置值
paramsObj[key] = val;
}
} else { // 处理没有 value 的参数
paramsObj[param] = true;
}
})
return paramsObj;
}
字符串模版
function render(template, data) {
const reg = /\{\{(\w+)\}\}/; // 模板字符串正则
if (reg.test(template)) { // 判断模板里是否有模板字符串
const name = reg.exec(template)[1]; // 查找当前模板里第一个模板字符串的字段
template = template.replace(reg, data[name]); // 将第一个模板字符串渲染
return render(template, data); // 递归的渲染并返回渲染后的结构
}
return template; // 如果模板没有模板字符串直接返回
}
Ajax
const getJSON = function(url) {
return new Promise((resolve, reject) => {
const xhr = XMLHttpRequest ? new XMLHttpRequest() : new ActiveXObject('Mscrosoft.XMLHttp');
xhr.open('GET', url, false);
xhr.setRequestHeader('Accept', 'application/json');
xhr.onreadystatechange = function() {
if (xhr.readyState !== 4) return;
if (xhr.status === 200 || xhr.status === 304) {
resolve(xhr.responseText);
} else {
reject(new Error(xhr.responseText));
}
}
xhr.send();
})
}