new
new被调用后做了三件事情:
- 让实例可以访问到私有属性
- 让实例可以访问构造函数原型(constructor.prototype)所在原型链上的属性
- 如果构造函数返回的结果不是引用数据类型
实现:
function New(ctor, ...args) {
//如果构造函数不是函数类型 那么抛出错误
if (typeof ctor !== "function") {
throw "New function the first param must be a function";
}
let obj = Object.create(ctor.prototype); //1.创建一个新对象,并且将它的proto指向构造函数的prototype person {name: "zs", age: 12} {}
let res = ctor.apply(obj, args); //2.将构造函数的this指向这个对象并执行,拿到返回结果 undefined {}
let isObject = typeof res === "object" && res !== null; // false true
let isFunction = typeof res === "function"; // false false
return isObject || isFunction ? res : obj;//3.如果这个结果是对象或函数就返回这个结果 否则返回创建的对象
}
测试:
function person(name, age) {
this.name = name;
this.age = age;
}
var c = New(person, "zs", 12);
var d = New(Object);
console.log(c); //person {name: "zs", age: 12}
console.log(d); //{}
注意的点:执行var d = New(Object);
的时候 Object.create(Object.prototype)
会返回一个空对象,再把空对象传给Object的构造函数
call
实现:
// 实现call 传参是一个一个参数
// 1.保留this,2.执行函数,3.删除函数
Function.prototype.newCall=function(context,...args){
var context=context||window;
let fn = Symbol("fn");
context.fn=this;//1.保留this 这个this是指person 这里把person隐式绑定到context上
let result=eval('context.fn(...args)');//2.执行函数 把元素一个个传进去(ES6 的...args解构)
delete context.fn;//3.删除函数
return result;
}
测试:
function person(a,b,c,d){
return{
name:this.name,
a:a,b:b,c:c,d:d
}
}
var egg={name:'老师'};
var bibi=person.newCall(egg,'点赞','收藏','转发','充电')//是person去执行
console.log(bibi);
apply
其实和call差不多
实现:
//实现apply 传入的是数组
Function.prototype.newApply=function(context,args){
var context=context||window;
let fn = Symbol("fn");
context.fn=this;//1.保留this 这个this是指person 这里把person隐式绑定到context上
let result=eval('context.fn(...args)');//2.执行函数 把元素一个个传进去
delete context.fn;//3.删除函数
return result;
}
测试:
function person(a,b,c,d){
return{
name:this.name,
a:a,b:b,c:c,d:d
}
}
var egg={name:'老师'};
var bibi=person.newApply(egg,['点赞','收藏','转发','充电'])//是person去执行
console.log(bibi);
bind
实现:
//实现bind 难点 bind配合new使用时 this丢失
Function.prototype.newBind=function(context,...args){
if(typeof this!=='function'){
throw new TypeError('错误')
}
var self=this//1.保存this
o=function(){}//通过一个空函数间接
var newf=function(){ // 2.声明函数
self.apply(this instanceof self ? this:context,//3.处理this丢失 发生了new构造函数就用实例的this 没有就还是用原来的方法执行即可
args.concat(Array.prototype.slice.call(arguments))//把arguments变成类数组形式
)
}
o.prototype=this.prototype //为了不直接修改person2的原型对象
newf.prototype=new o()// 4.处理继承,使newf(bibi2)的实例 b 可以访问到self(person2)上的属性
return newf //5.返回函数
};
测试:
function person2(a,b,c){
console.log(this.name);//undefined this丢失,访问不到egg2的name
console.log(a,b,c);//点赞 投币 充电
}
person2.prototype.collection='收藏'
var egg2={name:'老师'};
var bibi2=person2.newBind(egg2,'点赞','投币')
var b=new bibi2('充电')
console.log(b.collection);//收藏 因为通过原型链访问到person2上的属性
注意的点:new调用中的this绑定优先级的最高的,比bind高