开启掘金成长之旅!这是我参与「掘金日新计划 · 12 月更文挑战」的第7天,点击查看活动详情
new关键字
new关键字让构造函数与普通函数不同。
构造函数
var Person = function(name, age) {
this.name = name;
this.age = age;
this.getName = function() {
return this.name;
}
}
var p1 = new Person('Ness', 20);
console.log(p1.getName()); // Ness
console.log(p1 instanceof Person); // true
new关键字做了什么
- 声明一个中间对象;
- 将该中间对象的原型指向构造函数的原型;
- 将构造函数的this,指向该中间对象;
- 返回该中间对象,即返回实例对象。
// 将构造函数以参数的形式传入
function New(func) {
// 定义一个中间对象
let _obj = {};
// 将该中间对象的原型指向构造函数的原型;
_obj.__proto__ = func.prototype;
// 将构造函数的this,指向该中间对象;
// 使用apply改变this的指向;
// 使用call,将类数组转化为数组
// 返回实例
let ret = func.apply(_obj,Array.prototype.slice.call(arguments, 1));
// 当我们在构造函数中明确指定了返回对象时,那么new的执行结果就是该返回对象
if ((typeof ret === "object" || typeof ret === "function") && ret !== null) {
return ret;
}
// 返回该中间对象,即返回实例对象。
return _obj;
}
检验
var Person = function(name, age) {
this.name = name;
this.age = age;
this.getName = function() {
return this.name;
}
}
var p1 = New(Person,'张三', 12)
console.log(p1.getName());
call和apply 改变this的指向
call源码实现
- 接收一个参数,当前前要成为this指向的对象
- 定义一个临时变量,储存当前的this
- 收集函数参数,并从第1位截取。因为第0位表示构建函数
- 执行当前的this
- 删除临时定义的变量。并返回函数值
// call函数接收一个参数,表示当前要成为this指向的对象
Function.prototype.call = function(currentObj = window) {
// 定义一个临时变量
let fn;
// this指向问题,谁调用指向谁。现在是Array.prototype.slice
currentObj.fn = this;
// 收集参数
let args = [...arguments].slice(1);
// 执行
const funCall = currentObj.fn(...args);
// 删除临时变量
delete currentObj.fn;
// 返回
return funCall;
}
currentObj参数,默认为window。因为在使用中,call的第一个参数有可能回传null。
apply掩码实现
applye的实现和call的实现类似,只不过是接收的参数不同。apply接收一个数组[]。
Function.prototype.apply = function(currentObj = window) {
// 定义一个临时变量
let fn;
// this指向问题,谁调用指向谁。现在是Array.prototype.slice
currentObj.fn = this;
// 收集参数
let args = [...arguments].slice(1);
// 执行
const funCall = args.length ? currentObj.fn(...args): currentObj.fn();
// 删除临时变量
delete currentObj.fn;
// 返回
return funCall;
}
call和apply的区别
call和apply的区别是参数的区别。call接收的是一个一个参数。apply接收的是一个数组。
call 的性能要比 apply 好一些(尤其是传递给函数的参数超过三个的时候)
let arr = [10, 20, 30],
obj = { }
function fn (x, y, z) { }
fn.apply (obj, arr);
fn.call (obj, ...arr); =》基于 ES6 的展开运算符也可以实现把数组中的每一项一次传递给函数
bind函数
改变this执行,返回一个函数。
- 接收一个参数,bind的函数
- 保存当前this,谁调用就是谁
- 返回一个函数,等待调用
- 调用返回的函数,收集参数
- 改变this指向,调用。
Function.prototype.bind = function(context = window) {
//返回一个绑定this的函数,我们需要在此保存this
let self = this
// 可以支持柯里化传参,保存参数
let arg = [...arguments].slice(1)
// 返回一个函数
return function() {
let newArg = [...arguments];
return self.apply(context,[...arg,...newArg]);
}
}