对象是 new 出来的,那到底发生了什么?
什么,看到这个标题,大吃一惊,程序员竟然有对象?当然有啦,这都什么年代了。不信,你来看看有了对象之后,面试官就会问你到底发生了什么?
专业一点讲述:
new
是用来实例化一个类的,然后在内存中分配一个实例对象。
我们先来看看一个例子:
function Person(name) {
this.name = name;
}
Person.hairColor = "black";
Person.prototype.say = function() {
console.log("My name is " + this.name);
};
var ken = new Person("人生代码");
console.log(
ken.name, // "人生代码",
ken.hairColor, // undefined
ken.height // undefined
);
ken.say(); // "My name is 人生代码"
console.log(
Person.name, // "Person"
Person.hairColor // "black"
);
Person.say(); // Person.say is not a function
这段代码发生了什么?接下来就来揭晓答案
重点解析
第8行代码是关键:
var ken = new Person("人生代码");
Person
本来就是个普通的函数,只不过如果给他加了 new
操作,就变成了构造函数。
那么 JS
引擎在解析代码的时候,内部会做很多处理,伪代码如下:
new Person('人生代码') = {
var obj = {}; // 定义对象
var proto = Object.create(Person.prototype); // 复制原型
obj.__proto__ = proto; // 建立原型链
// obj->Person.prototype->Object.protorype->null
var res = Person.call(obj, '人生代码'); // 相当于 obj.Person('人生代码')
// 如果无返回值或者返回一个非对象值,则将obj返回作为新对象:
return typeof res === 'object' ? result || obj : obj;
}
我们可以得出以下几点:
obj.name
是在Person.call(obj, '人生代码')
之后才赋值的- 等到
obj
经返回被赋给ken
之后,ken.name
就是 '人生代码' 了
所以我们可以画出以下原型链:
ken.name
: 临时变量obj
有name
,obj
经返回被赋给ken
,ken
的一些属性由此而来。ken.hairColor
:ken
实例对象 先查找自身的hairColor
,没有找到便会沿着原型链查找,在上述例子中,我们仅在Person
对象上定义了hairColor
,并没有在其原型链上定义,因此找不到。ken.height
:ken
实例对象 先查找自身的height
,没有找到便会沿着原型链查找,原型链上也没有,因此找不到。ken.say
:ken
会先查找自身的say
方法,没有找到便会沿着原型链查找,在上述例子中,我们在Person.prototype
上定义了say
,因此在原型链上找到了say
方法。- 另外,在
say
方法中还访问this.name
,这里的this
指的是其调用者。如果ken
调用say
,ken
就是调用者,因此输出ken.name
的值。
总结
new 运算符创建一个用户定义的对象类型的实例或具有构造函数的内置对象的实例。new 关键字会进行如下的操:
- 创建一个空的简单
JavaScript
对象(即{}); - 链接该对象(即设置该对象的构造函数)到另一个对象 ;
- 将步骤1新创建的对象作为
this
的上下文 ; - 如果该函数没有返回对象,则返回
this
。
代码实现请参考以下链接:
https://github.com/sisterAn/JavaScript-Algorithms/issues/71
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。