1.call 实现
1.call 改变 this 指向
2.传参要解析传过去
var foo = {
value: 1
};
var value = 2;
function bar() {
console.log(this.value);
}
// bar._call(foo);
在这里要 console.log要打印出来 1 来。就是说 foo 里面有个 bar 方法执行。
var foo = {
value: 1,
bar: bar()
};
怎么写呢
bar._call(foo, 'aa', 'bb');
Function.prototype._call = function () {
// 首先要解析传入的值
// 改变this 的指向 到call的函数也就是foo
// foo上面新加一个属性就是bar这个方法
ctx = arguments[0] || window;
ctx.fn = this;
ctx.fn();
delete ctx.fn;
};
Function.prototype._call2 = function (ctx, ...args) {
// 首先要解析传入的值
// 改变this 的指向 到call的函数也就是foo
// foo上面新加一个属性就是bar这个方法
var fn = Symbol();
ctx = ctx || window;
ctx[fn] = this;
let result = ctx[fn](...args);
delete ctx[fn];
return result;
};
2.apply 的实现
与 call 实现不同,apply 传入的参数是个数组
Function.prototype._apply = function (ctx, arr) {
var ctx = ctx || window;
var fn = Symbol();
ctx[fn] = this;
var result;
if (!arr) {
result = ctx[fn]();
} else {
result = ctx[fn](...arr);
}
delete ctx[fn];
return result;
};
3.bind 的实现
多次 bind 指向的还是第一个绑定的 this
bind() 方法会创建一个新函数。当这个新函数被调用时,bind() 的第一个参数将作为它运行时的 this,之后的一序列参数将会在传递的实参前传入作为它的参数。(来自于 MDN )
1.返回一个函数
2.可以传入参数
Function.prototype._bind = function (context) {
var self = this;
var args = Array.prototype.slice.call(arguments, 1);
return function () {
var bindArgs = Array.prototype.slice.call(arguments);
self.apply(context, args.concat(bindArgs));
};
};
// 第三版
Function.prototype.bind2 = function (context) {
var self = this;
var args = Array.prototype.slice.call(arguments, 1);
var fbound = function () {
var bindArgs = Array.prototype.slice.call(arguments);
// 当作为构造函数时,this 指向实例,self 指向绑定函数,因为下面一句 `fbound.prototype = this.prototype;`,已经修改了 fbound.prototype 为 绑定函数的 prototype,此时结果为 true,当结果为 true 的时候,this 指向实例。
// 当作为普通函数时,this 指向 window,self 指向绑定函数,此时结果为 false,当结果为 false 的时候,this 指向绑定的 context。
self.apply(this instanceof self ? this : context, args.concat(bindArgs));
};
// 修改返回函数的 prototype 为绑定函数的 prototype,实例就可以继承函数的原型中的值
fbound.prototype = Object.create(self.prototype);
return fbound;
};
bind = function (context) {
var self = this;
var args = [].slice.call(arguments, 1);
var found = function () {
var bindArgs = [].slice.call(arguments);
self.apply(context, args.concat(bindArgs));
};
};
apply = function (ctx, ...args) {
ctx = ctx || window;
const fn = Symbol();
// 把函数挂载到对象上面去
ctx[fn] = this;
// 执行函数
let result = ctx[fn](...args);
delete ctx[fn];
return result;
};
一个绑定函数也能使用new操作符创建对象:这种行为就像把原函数当成构造器。提供的 this 值被忽略,同时调用时的参数被提供给模拟函数。
4.new 的实现
- 返回一个新的对象,
- 把实例的原型指向构造函数的原型
- this 指向实例本身
// 将构造函数以参数形式传入
function New(func) {
// 声明一个中间对象,该对象为最终返回的实例
var res = {};
if (func.prototype !== null) {
// 把实例的原型指向构造函数的原型
res.__proto__ = func.prototype;
}
// ret 是构造函数执行的结果,也就是构造函数里面return出来的
// 将构造函数的this指向为实例对象 res 的this
const ret = func.apply(res, [].slice.call(arguments, 1));
if ((typeof ret === 'object' || typeof ret === 'function') && ret !== null) {
return ret;
}
return res;
}
function Person(name, age) {
this.name = name;
this.strength = 60;
this.age = age;
return {
name: name,
time: 80
};
return 'aaa';
}
var student = New(Person, 'tom');
如果构造函数返回的是一个对象或者函数,在实例中 student 中就只能访问返回的对象中的属性。
如果返回的是一个字符串,相当于没有返回值
5.instanceof 实现
function _instanceof(left, right) {
if (typeof left !== 'object' || left === null) {
return false;
}
let proto = Object.getPrototypeOf(left);
while (true) {
if (proto === null) return false;
if (proto == right.prototype) return true;
proto = proto.prototype;
}
}
6.Promise 实现
class MyPromise {
constructor(executor) {
this.thencallback = undefined;
this.rejectcallback = undefined;
this._value = undefined;
this._status = 'pending';
executor(this._resolve.bind(this), this._reject.bind(this));
}
_resolve(value) {
setTimeout(() => {
this.thencallback(value);
});
}
_reject(value) {
setTimeout(() => {
this.rejectcallback(value);
});
}
then(then_cb, onRejected) {
this.thencallback = then_cb;
this.rejectcallback = onRejected;
}
catch(onrejected) {
this.then(null, onrejected);
}
}
Promise.resolve = function (value) {
if (value instanceof Promise) return value;
// 如果是thenable
if (value && value.then && isFunction(value.then)) return new Promise(value.then);
return new Promise(resolve => resolve(value));
};
Promise.reject = function (value) {
return new Promise((null, reject) => reject(value));
};
Promise.all = function (promises) {
let i = 0;
let returnList = [];
return new Promise((resolve, reject) => {
for (let i = 0; i < promises.length; i++) {
// 如果有一个报错那么所有的.all也进入reject
promises[i].then(data => {
procssData(num, data);
}, reject);
}
function procssData(num, data) {
i++;
// 数组的顺序要对应
returnList[num] = data;
// 如果标记的数字等于长度的话就返回
if (i === promises.length) {
resolve(returnList);
}
}
});
};
Promise.race = function (promises) {
return new Promise((resolve, reject) => {
for (let i = 0; i < promises.length; i++) {
promises[i].then(resolve, reject);
}
});
};
7.防抖、节流
防抖
事件在触发一段时间后才执行,如果在这时间段内又被触发,则重新计时
function debounce(fn, time = 300) {
let timer = null;
return function (...args) {
if (timer) clearTimeout(timer);
timer = setTimeout(() => {
fn.apply(this, args);
}, time);
};
}
debounce(() => {
console.log(11);
}, 1000);
节流
事件再一段时间内只会被出发一次,就像公交车 10 分钟一趟,适用于点击时间
function throttle(fn, time = 1000) {
let last = 0;
return function (...args) {
let now = Date.now();
if (now - last < time) return;
last = now;
fn.apply(this, args);
};
}
合并优化
有时候长时间的防抖会导致一次都没有执行, 一段时间必须执行一次
function cover(fn, delay) {
let timer = null;
let last = 0;
return function (...args) {
let now = +new Date();
if (now - last < delay) {
if (timer) clearTimeout(timer);
timer = setTimeout(() => {
last = now;
fn.apply(this, args);
}, delay);
} else {
last = now;
fn.apply(this, args);
// 每次触发都会清空上一个定时器,开始执行新的定时器
```js
function deounce(func, wait) {
let timer;
return function () {
let arg = arguments;
if (timer) clearTimeout(timer);
timer = setTimeout(func.apply(this, arg), wait);
};
}
8.深、浅拷贝
浅拷贝
function shallowCopy(obj) {
if (typeof obj !== 'object') return obj;
let newObj = obj.constructor === Array ? [] : {};
for (key in obj) {
if (obj.hasOwnProperty(key)) {
newObj[key] = obj[key];
}
}
return newObj;
}
// 深拷贝
9. for in \ for of
for of 循环用来获取一对键值对中的值,而 for in 获取的是 键名
一个数据结构只要部署了 Symbol.iterator 属性, 就被视为具有 iterator 接口, 就可以使用 for of 循环。
- 数组 Array、 Map、 Set、 String、 arguments 对象、 Nodelist 对象,这些都可以用 for of 循环值
10.数组方法实现
map
arr.map((item, index) => {});
Array.prototype._map = function (fn, context) {
var temp = [];
if (typeof fn == 'function') {
for (let i = 0; i < this.length; i++) {
temp.push(fn.call(context, this[i], i, this));
}
} else {
console.log('TypeError' + fn + 'is not a function');
}
};
reduce
promise
// 简化版本
class Promise {
constructor(executor) {
this.thenCb = null;
this.rejectCb = null;
executor(this._resolve.bind(this), this._reject.bind(this));
}
_resolve(value) {
setTimeout(() => {
this.thenCb(value);
}, 0);
}
_reject(value) {
setTimeout(() => {
this.rejectCb(value);
}, 0);
}
then(onResolved, onRejected) {
this.thenCb = onResolved;
this.rejectCb = onRejected;
}
catch(onRejected) {
this.then(null, onRejected);
}
}