new
new一个函数时,其内部语句其实为this(fn)._proto_ = Fn.prototype。
例:
function Fn() {} // 内部语句:this.prototype = {}
Fn.prototype.test= function() {
console.log(test)
}
let fn = new Fn() // 内部语句:this(fn).<u>proto</ u>= Fn.prototype
fn.test()
实现:
function myNew() {
let construct = [].shift.call(arguments)
// ...省去若干的判断条件
// let obj = {}
// obj.__proto__ = construct.prototype
let obj = Object.create(construct.prototype) // 创建obj的__proto__ = construct.prototype
const result = construct.apply(obj, arguments)
return typeof result === 'object' ? result || obj : obj // 匹配函数return出一个对象和null
}
function A(){ }
A.prototype.get = function() {
console.log('get')
}
function B(){
return {
b: '1'
}
}
let aa = myNew(A)
let bb = myNew(B)
console.log(aa)
call,apply
我们知道call和apply改变this的指向,并执行函数。
例:
let obj = {
name: 'obj',
getName: function() {
console.log(this.name)
}
}
let obj2 = {
name: 'obj2'
}
obj.getName.call(obj2)
其实就是把obj下的getName方法复制到obj2下,运行完后删除。
实现:
Object.prototype.myCall = function (context, ...args) {
// ...省去若干的判断条件
context = context || window
const key = Symbol()
context[key] = this
let result = context[key](...args)
delete context[key]
return result
}
obj.getName.myCall(obj2)
apply这是在传参上是数组,其原理实现更call 一样。
Object.prototype.myApply = function (context, ...args) {
// ...省去若干的判断条件
context = context || window
args = args || []
const key = Symbol()
context[key] = this
let result = context[key](...args)
delete context[key]
return result
}
bind
bind改变this指向,返回的是一个函数,我们在自己去执行它。
例:
let obj = {
name: 'obj',
getName: function(a, b) {
console.log(a, b)
console.log(this.name)
}
}
let obj2 = {
name: 'obj2'
}
let fn = obj.getName.bind(obj2,'1')
fn('2')
let test = new fn('3')
可以看到使用函数柯里化处理传参,并返回一个未执行的函数。我们执行时改变this指向,使用上例call或apply就可以了。当然这里需考虑new的情况
实现:
Object.prototype.myBind = function (context, ...args) {
// ...省去若干的判断条件
const fn = this
args = args || []
let newFn = function(...newArgs) {
if (this instanceof newFn) { // 遇到new
fn.apply(fn, [...args, ...newArgs])
} else {
fn.apply(context, [...args, ...newArgs])
}
}
// 针对创建实例做处理,此时newFn函数有自己的原型,我们需使它能够继承绑定函数的原型
newFn.prototype = fn.prototype
return newFn
}
let test = obj.getName.myBind(obj2, '1')
let dd = new test('2')
test('3')