箭头函数没有原型链吗?+ 一次错误的尝试

1,032 阅读4分钟

箭头函数有原型链吗?

我的结论是箭头函数的原型链是不完整的,但并不是没有!

问题的思考来源于instanceof

在我写代码的一个场景里需要判断某个变量类型是不是函数,于是我想到了使用instanceof,mdn中对instanceof定义是去比对实例对象的原型链的构造函数是否相同。

function fn(){}
fn instanceof Fucntion // true

此时我就有了一个想法如果fn是箭头函数呢?在我的脑海里总是记得箭头函数是没有原型链的,所以我就以为下面这段代码的结果会是false

let fn = () => {}
fn instanceof Fucntion // 结果还是true

结果还是true

于是我震惊了,我就去百度?说不定会有别的角度的理解! 百度的结果是:

image.png 我觉得不严谨,我就去了伟大的mdn,看看mdn对箭头函数的定义!

image.png

也就是说箭头函数中只是没有prototype属性,没有构造函数的这一特有定义的属性。但是

let fn = () => {} 
fn.__proto__ = Function.prototype // true
fn.__proto__.__proto__ === Object.prototype

也就是说,箭头函数不能类比普通的函数,含有prototype属性等!(不绑定arguments.不能使用使用 yield 关键字等).类似于Funtion构造函数的实例对象。

结论一:箭头函数有原型链,但是没有prototype属性,箭头函数作为构造函数去创建实例对象!

let fn = () => {}
let obj = new fn() // VM1103:2 Uncaught TypeError: fn is not a constructor

于是乎我有了新的想法,让箭头函数可以成为构造函数!

:对比区别,是什么让箭头函数无法成为构造函数!

因为箭头函数不是constructor。这个地方我目前想不出办法来解决,没有找到new关键字在运行过程的详细代码,不知道如何一个变量是如何判断是否是一个constructor?希望有大佬能教我一下。

function fn() {}
let obj = new fn() // fn是如何判断是constructor?

总结下来两个点可以解决问题

  1. 让构造函数拥有prototype来维系原型链
  2. 手写一个新的new函数,跳过判断原型的过程
那么prototype有什么呢?
  • contructor: 指向实例对象的构造函数
  • '__ proto __': 当前执行构造函数上一级的构造函数的prototype
  • 其他自定义的属性

个人理解:原型链的起点是实例对象的__proto__,往上走每一级的的对象即是实例对象也是构造函数。沿着 __ proto __.__proto__一直向上查找到对应的方法或属性。直到null!

let theArrowFn.prototype = () => {}
theArrowFn.prototype = {
    constructor: arrow1,  // 指向实例对象的构造函数
    __proto__: Function.prototype // 当前执行构造函数上一级的构造函数的prototype
    mm: 11,
    sayHello: ()=> {
        console.log(1111,this.a,this.b);
    }
}
new的过程呢?
  • 创建一个新对象
  • 指定新对象的__proto__ 为构造函数的原型prototype
  • 绑定构造函数this指向为新对象,运行函数
  • 构造函数返回值为对象 ? 输出返回的对象 :新对象
function handleNew(fun,...args) {
    // 边界错误返回 null
    if(!(fun instanceof Function)) {
        return null
    }
    let a = {} // 创建一个新对象
    a.__proto__ = fun.prototype // 指定新对象的__proto__
    let c =   fun.apply(a,args) // 运行构造函数
    return c instanceof Object ? c : a // 输出结果
}
验证结果
let theArrowFn = (a,b) => {
 this.a = a
 this.b = b
}
theArrowFn.prototype = {
    constructor: theArrowFn,  // 指向实例对象的构造函数
    __proto__: Function.prototype, // 当前执行构造函数上一级的构造函数的prototype
    mm: 11,
    sayHello: ()=> {
        console.log(1111,this.a,this.b);
    }
}

// 手写new函数
function handleNew(fun,...args) {
    // 边界错误返回 null
    if(!(fun instanceof Function)) {
        return null
    }
    let a = {} // 创建一个新对象
    a.__proto__ = fun.prototype // 指定新对象的__proto__
    let c =   fun.apply(a,args) // 运行构造函数
    return c instanceof Object ? c : a // 输出结果
}

let resuleObj  = handleNew(theArrowFn,1,2)

consoloe.log(resuleObj.a) // undefined
consoloe.log(resuleObj.b) // undefined
consoloe.log(resuleObj.mm) // 11
consoloe.log(resuleObj.sayHello()) // 1111 1 2

当我信心满满的验证结果的时候发现验证结果不对!this的绑定不对,apply没有生效!此时明白我犯了一个致命的错误!

—-> 箭头函数的this指向无法改变

js是基于词法作用域的,也就是静态作用域,词法作用域是指函数this指向由函数定义的时候的位置决定的。箭头函数不会创建自己的this,只会取/继承指向定义时的函数作用域/全局作用域!

并且在mdn中说明了由于 箭头函数没有自己的 this 指针,通过 call()  apply() 方法调用一个函数时,只能传递参数(不能绑定 this---译者注),他们的第一个参数会被忽略!**

所以由于无法改变箭头函数this指向,那么正在new的过程的中,运行构造函数时无法为新对象赋予新的属性或方法

总结

  1. 对待别人输出的观点要自己实践思辨后再吸收!
  2. 虽然是一次错误的尝试,但是梳理之后还是有所收获!