js new一个function都发生了什么

2,577 阅读2分钟
原文链接: www.oecom.cn

在ES5当中其实是没有类这个概念的,我们使用js的OOP编程其实是用function模拟了类的实现。

function TestFun(name){
    let age = 10;
    this.name = name
    this.getAge = function(){
        return age;
    }
}
let fun = new TestFun("张三");
console.log(fun.name)
console.log(fun.getAge())

js new一个function都发生了什么
这样我们就实现了一个类的效果,那么new这个function的时候到底都发生了什么呢,为什么fun.name的值就是张三呢,如果我们直接调用TestFun.name会发生什么呢,其实这里会直接输出TestFun,因为这个name属性就TestFun的一个属性,如果我们在添加一个this.context那么如果全局变量里没有context,则会输出undefined。

这里就涉及到了作用域的概念,在TestFun中,如果没有new这个关键字,这里的this是指向其上层作用域的。一旦使用new关键字后这个作用域就发生了变化。

我们把TestFun输出看一下。
js new一个function都发生了什么
我们可以看到其中有两个属性,一个是prototype,另一个是proto。一个对象的 proto 属性和自己的内部属性[[Prototype]]指向一个相同的值 (通常称这个值为原型),原型的值可以是一个对象值也可以是null(比如说Object.prototype.proto 的值就是null).该属性可能会引发一些错误,因为用户可能会不知道该属性的特殊性,而给它赋值,从而改变了这个对象的原型. 如果需要访问一个对象的原型,应该使用方法Object.getPrototypeOf.

firefox、chrome等浏览器把对象内部属性[[Prototype]]用 proto 的形式暴露了出来.(老版本的IE并不支持 proto ,IE11中已经加上了 proto 属性)

其实我们只需要记住: proto 是原型,prototype是函数默认的一个属性,它指向一个对象,这个对象的constructor属性指向函数本身.

说了这么多,new到底发生了什么呢?

MDN上是这么说的

function Foo(){}
var o = new Object();
o.[[Prototype]] = Foo.prototype;
Foo.call(o);

上面我们说过了,一个对象的内部属性[[Prototype]]和proto指向一个值,所以说在new的时候我们可以理解为

let fun = new Object();
fun.__proto__ = TestFun.prototype;
TestFun.call(fun)

上面call的意思就是将TestFun中this绑定到fun上。但是一般情况下如果function没有return的时候这是一个正常的流程,如果function return了一个Object类型,则new完成以后则会返回这个object,如果是非Object(string,number,布尔类型等)则会正常返回这个构造函数。

function TestFun(name){
    let age = 10;
    this.name = name
    this.getAge = function(){
        return age;
    }
    return {

    }
}
let fun = new TestFun("张三");
console.log(fun.name)
console.log(fun.getAge)

如果是这样的话,那么我们输出的name和getAge均为undefined。