使用new操作符,发生的过程
- 创建一个新的对象
- 把该对象的_proto_属性设置为构造函数的prototype属性,即完成原型链
- 执行构造函数中的代码,构造函数中的this指向该对象(创建的新对象)
- 返回对象 (注意函数没有返回对象类型,才会自动返回这个新对象)
function _new() {
let obj = new Object() //创建一个对象
constructor = [].shift.call(arguments) //获取构造函数
obj.__proto__ = constructor.prototype //将对象的_proto_属性设置为构造函数的prototype属性
let ret = constructor.apply(obj,arguments) //执行构造函数
return ret instanceof Object ? ret : obj //返回对象
}
es6写法
function _new(fn,...arg) {
let obj = Object.create(fn.prototype)
let ret = fn.apply(obj,arg)
return ret instanceof Object ? ret : obj
}
例子:
function Person (name,age) {
this.name = name
this.age = age
}
Person.prototype.getName = function() {
return this.name
}
let p1 = new Person('张三',18)
p1.getName()// 张三
let p2 = _new(Person,'李四',20)
p2.getName()// 李四
call() 方法使用一个指定的 this 值和单独给出的一个或多个参数来调用一个函数。
call 方法第一个参数是要绑定给this的值,后面传入的是一个参数列表。当第一个参数为null、undefined的时候,默认指向window
模拟的步骤可以分为:
- 将函数设为对象的属性
- 执行该函数
- 删除该函数
Function.prototype.myCall = function(context) {
context = Object(context) || window;
let arg = [...arguments].slice(1)
context.fn = this
let ret = context.fn(...arg)
delete context.fn
return ret
}
let dog = {
name: '狗',
eat(food) {
console.log(this.name + '爱吃' + food )
},
}
let cat = {
name: '猫'
}
dog.eat.call(cat,'鱼') // 猫爱吃鱼
dog.eat.myCall(cat,'鱼') // 猫爱吃鱼
apply接受两个参数,第一个参数是要绑定给this的值,第二个参数是一个参数数组
apply和call用法相似,唯一的区别是apply传参是数组
Function.prototype.myApply = function(context,arg) {
context = Object(context) || window;
context.fn = this
let ret = arg ? context.fn(...arg) : context.fn()
delete context.fn
return ret
}
bind和call很相似,第一个参数是this的指向,从第二个参数开始是接收的参数列表。区别在于bind方法返回值是函数以及bind接收的参数列表的使用
模拟步骤
- 可传入一个对象,作为函数的上下文对象
- 可传入多个参数
- 返回一个新的函数
先实现指定函数上下文对象的功能
Function.prototype.myBind = function (context) {
//获取到call函数
let self = this;
//需要返回的函数
let fBound = function() {
return self.apply(context);
}
return fBound;
}
let foo = {
value: 1
};
function bar() {
console.log(this.value);
}
let myBind1 = bar.myBind(foo)
myBind1() // 1
实现传参部分
Function.prototype.myBind = function (context) {
//获取到call函数
let self = this;
let bindArg = [...arguments].slice(1);
//需要返回的函数
let fBound = function() {
let arg = [...arguments];
return self.apply(context,bindArg.concat(arg));
}
return fBound;
}
let foo = {
value: 1
};
function bar(name,age) {
console.log(this.value);
console.log(name)
console.log(age)
}
let myBind1 = bar.myBind(foo,'lisi')
myBind1(18)
// 1 lisi 18
构造函数模拟实现
bind函数还有个特点:
一个绑定函数也能使用new操作符创建对象:这种行为就像把原函数当成构造器。提供的 this 值被忽略,同时调用时的参数被提供给模拟函数。
let myBind2 = bar.bind(foo,'lis')
new myBind2(18)
// undefined lisi 18
因为使用了new关键字,使用new调用构造函数,会创建一个新的对象,this会指向这个新的对象,而新的对象上并没有value属性,所以会打印undefined。
继续在myBind上改造,判断如果返回函数被当做构造函数使用,那么上下文对象则指定为this
Function.prototype.myBind = function (context) {
//获取到call函数
let self = this;
//需要返回的函数
let bindArg = [...arguments].slice(1);
let fBound = function() {
let arg = [...arguments];
return self.apply(this instanceof fBound ? this : context, bindArg.concat(arg));
}
return fBound;
}
let foo = {
value: 1
};
function bar(name,age) {
console.log(this.value);
console.log(name)
console.log(age)
}
let myBind1 = bar.myBind(foo,'lisi')
new myBind1(18)
// undefined lisi 18
注意需要关联调用myBind方法的函数的原型对象
Function.prototype.myBind = function (context) {
//获取到call函数
let self = this;
//需要返回的函数
let bindArg = [...arguments].slice(1);
let fBound = function() {
let arg = [...arguments];
return self.apply(this instanceof fBound ? this : context, bindArg.concat(arg));
}
return fBound;
}
let foo = {
value: 1
};
function bar(name,age) {
console.log(this.value);
console.log(name)
console.log(age)
}
bar.prototype.skill = 'eat'
let myBind1 = bar.myBind(foo,'lisi')
let obj = new myBind1(18)
consloe.log(obj.skill) // undefined
可以看到bar函数原型对象上的属性并没有找到,因为我们返回了一个新的函数,这个新的函数被当做构造函数调用,并将新的创建的对象指定为bar函数的上下文对象,所以无法找到skill属性。
所以我们需要将myBind返回的函数与bar函数的原型对象进行关联
Function.prototype.myBind = function (context) {
//获取到call函数
let self = this;
//需要返回的函数
let bindArg = [...arguments].slice(1);
let fBound = function() {
let arg = [...arguments];
return self.apply(this instanceof fBound ? this : context, bindArg.concat(arg));
}
fBound.prototype = Object.create(self.prototype);
return fBound;
}
let foo = {
value: 1
};
function bar(name,age) {
console.log(this.value);
console.log(name)
console.log(age)
}
bar.prototype.skill = 'eat'
let myBind1 = bar.myBind(foo,'lisi')
let o = new myBind1(18)
console.log(o.skill) // eat