call
ES3实现方式
args 里面 push 使用下面这种方式,而不直接使用 push( arguments[i] ) 的原因是数组 toString 的时候,除了 string,number 类型的其他类型会有问题
Function.prototype.call = function (context) {
context = context ? Object(context) : window;
context.fn = this;
var args = [];
for(var i = 1, len = arguments.length; i < len; i++) {
args.push('arguments[' + i + ']');
}
var result = eval('context.fn(' + args +')');
delete context.fn
return result;
}
ES6实现方式
Function.prototype.call = function (context) {
context = context ? Object(context) : window;
context.fn = this;
let args = [...arguments].slice(1);
let result = context.fn(...args);
delete context.fn
return result;
}
apply
ES3实现方式
Function.prototype.apply = function (context, arr) {
context = context ? Object(context) : window;
context.fn = this;
var result;
// 判断是否存在第二个参数
if (!arr) {
result = context.fn();
} else {
var args = [];
for (var i = 0, len = arr.length; i < len; i++) {
args.push('arr[' + i + ']');
}
result = eval('context.fn(' + args + ')');
}
delete context.fn
return result;
}
ES6实现方式
Function.prototype.apply = function (context, arr) {
context = context ? Object(context) : window;
context.fn = this;
let result;
if (!arr) {
result = context.fn();
} else {
result = context.fn(...arr);
}
delete context.fn
return result;
}
bind
要注意,继承原函数的原型链
Function.prototype.bind2 = function (context) {
if (typeof this !== "function") {
throw new Error("Function.prototype.bind - what is trying to be bound is not callable");
}
var self = this;
var args = Array.prototype.slice.call(arguments, 1);
var fNOP = function () {};
fNOP.prototype = this.prototype;
var fBound = function () {
var bindArgs = Array.prototype.slice.call(arguments);
return self.apply(this instanceof fNOP ? this : context, args.concat(bindArgs));
}
fBound.prototype = new fNOP();
return fBound;
}
new
function create() {
// 1、获得构造函数,同时删除 arguments 中第一个参数
Con = [].shift.call(arguments);
// 2、创建一个空的对象并链接到原型,obj 可以访问构造函数原型中的属性
var obj = Object.create(Con.prototype);
// 3、绑定 this 实现继承,obj 可以访问到构造函数中的属性
var ret = Con.apply(obj, arguments);
// 4、优先返回构造函数返回的对象
// 这是为了应付构造函数有返回对象的情况,下面给出实例
return ret instanceof Object ? ret : obj;
};
new 第4点实例解释
1.构造函数返回一个对象
function Car(color, name) {
this.color = color;
return {
name: name
}
}
var car = new Car("black", "BMW");
car.color;
// undefined
car.name;
// "BMW"
2.构造函数没有return,返回undifine
function Car(color, name) {
this.color = color;
}
var car = new Car("black", "BMW");
car.color;
// black
car.name;
// undefined
3.构造函数有返回值,但是返回的不是对象
function Car(color, name) {
this.color = color;
return "new car";
}
var car = new Car("black", "BMW");
car.color;
// black
car.name;
// undefined
debounce
function debounce(func, wait, immediate) {
var timeout;
return function () {
let context = this//如果不改变指向,则目标函数的this指向的是window
let args = arguments//如果不传递参数,事件触发函数的参数无法传递给目标函数
let result
clearTimeout(timeout)
if (immediate) {
if (!timeout)
result = func.apply(context, args)//只有立即执行才能返回值
timeout = setTimeout(function () {
timeout = null//为了到达指定时间后,可以再次执行目标函数
}, wait);
}
else {
timeout = setTimeout(function () {
func.apply(context, args)
}, wait);
}
return result
}
}
throttled
function throttle(func, wait) {
var timeout, context, args, result;
var previous = 0;
var throttled = function () {
var now = +new Date();
//下次触发 func 剩余的时间
var remaining = wait - (now - previous);
context = this;
args = arguments;
// 一次循环中,第一次执行的是if的func,后面都是执行else里面的func
// 如果没有剩余的时间了或者你改了系统时间
if (remaining <= 0 || remaining > wait) {
previous = now;
func.apply(context, args);
} else if (!timeout) {
timeout = setTimeout(function () {
previous = +new Date();
timeout = null;
func.apply(context, args)
}, remaining);
}
};
return throttled;
}