我正在参加「掘金·启航计划」
第二天
一、new
1. new 做了什么
- 创建一个新的空对象 obj{}。
- 将这个对象的原型设为构造函数的原型链。
- 将构造函数内部的 this 指向 new 创建的新对象,并执行构造函数。
- 判断构造函数内部有无返回值,没有返回值则返回 new 创建的对象。如果有返回值则判断返回值是否是普通类型和 null,如果是则直接将 new 创建的对象返回出去,可如果返回值是引用类型数据,则会将这个返回值当做对象返回出去。
2. 手动实现一个 new
// 实现 new
function myNew(Fn, ...arg) {
// 创建一个空对象,并将构造函数的原型赋予到这个空对象上
let obj = Object.create(Fn.prototype);
// 将构造函数的 this 指向这个空对象,并执行构造函数
let fun = Fn.call(obj, arg);
// 最后将这个对象返回出去
return fun instanceof Object ? fun : obj;
}
// 测试
function myFunction(name, sex){
this.name = name
this.sex = sex
}
// 对构造函数的原型添加方法
myFunction.prototype.run = function(){
console.log(this.name, this.sex)
}
// 创建实例对象
let zs = myNew(myFunction, '张三', '男')
// 调用实例原型上的方法
zs.run()
// 输出这个对象
console.log(zs)
这里我们可以发现 zs 这个对象的原型已然指向了 myFunction 原型链,constructor 构造器指向了 myFunction。
二、call、apply、bind
1. call、apply、bind 的作用
改变函数调用时的作用域。
1.1. 三者区别
- call:除了参数集不是数组,其他与 apply 无异。
function.call(this, arg1, arg2, ....)
- apply:与 call 差不多,只是参数集是数组。
function.apply(this, [arg1, arg2, ...])
- bind:与 call 无异,只是返回结果改为了函数。
let fun = function(this, arg1, arg2, ...)
1.2 使用
// 在 window 全局添加一个 name 属性
window.name = 'window';
// 声明一个对象
const obj = {
// 对象中也声明一个 name 属性
name: "obj",
};
// 声明一个函数
function say(arg1, arg2) {
console.log(this.name, arg1, arg2);
}
// 正常输出:window 你好 世界
say("你好", "世界");
// call 输出:obj 你好 世界
say.call(obj, "你好", "世界");
// apply 输出:obj 你好 世界
say.apply(obj, ["你好", "世界"]);
// bind 输出:obj 你好 世界
let msg = say.bind(obj, "你好", "世界")();
2. 手动实现 call、apply、bind
下面例子中使用的代码是上面例子的代码,下面为了省略没有摘抄。
2.1 实现 call
Function.prototype.myCall = function(context, ...args){
// 判断有无传入作用域,如果没有传入,则直接使用 windows 全局作用域
var context = context || window
// 将当前调用的函数暂时赋给 fn 属性,待会好执行
context.fn = this
// 使用 eval 执行当前函数,并将 args 参入
var res = eval('context.fn(...args)')
// 最后删除这个属性
delete context.fn
// 返回执行结果
return res
}
// 测试,输出:obj 1 2
say.myCall(obj, '1', '2')
2.2 实现 apply
// 实现 apply
Function.prototype.myApply = function(context, args){
// 获取作用域
var context = context || window
// 拿到当前要执行的函数
context.fn = this
// 放在 eval 中执行
let res = eval('context.fn(...args)')
// 然后删除这个用来存放当前要执行函数的属性
delete context.fn
// 将执行结果返回
return res
}
// 测试,输出:obj 3 4
say.myApply(obj, ['3', '4'])
2.3 实现 bind
Function.prototype.myBind = function(context, ...args){
// 获取目标执行作用域
var context = context || window
// 拿到当前要执行的函数
let fn = this
// 使用 call 执行,并返回一个函数
return function(){
return fn.call(context, ...args)
}
}
// 测试,输出:obj 5 6
let my = say.myBind(obj, '5', '6')()