1 对象是什么
对象是单个实物的抽象。通常有一个模板表示共同特征,然后对象根据这个模板生成。
典型的面向对象编程语言(比如C++和Java),都有“类”(class)这个概念。所谓“类”就是对象的模板,对象就是“类”的实例。但是JavaScript的对象体系,不是基于“类”的,而是基于构造函数(constructor)和原型链(prototype)。
2 构造函数
JavaScript使用构造函数(constructor)作为对象的模板。所谓“构造函数”,就是专门用来生成实例对象的函数。它是对象的模板,描述了实例对象的基本结构。
构造函数其实是一个普通的函数,但是有自己的特征和用法。
var Person = function() {
this.age = 30;
}
上面代码中,Person就是一个构造函数。为了与普通函数区别,构造函数名字的第一个字母通常大写。
构造函数的特点有两个。
- 函数内部使用了this关键字,代表所要生成的对象实例。
- 生成对象的时候,必须使用new命令。
3 new命令
3.1 基本用法
new命令的作用,就是执行构造函数,返回一个实例对象。
var Person = function() {
this.age = 30;
};
var p = new Person();
p.age // 30
上面代码中,通过new命令让构造函数Person生成一个实例对象,保存在变量p中。构造函数内部的this,代表新生成的实例对象。
new命令本身就可以执行构造函数,所以后面的构造函数可以带括号,也可以不带括号。下面两行代码是等价的,但为了表示这是函数调用,推荐使用括号。
var p = new Person(); // 推荐的写法
var p = new Person; // 不推荐的写法
如果忘了使用new命令,直接调用构造函数会发生什么呢?
这种情况下,构造函数变成普通函数,并不会生成实例对象。而且,这时this代表全局对象,还将造成一些意想不到的结果。
var Person = function() {
this.age = 30;
};
var p = Person();
p // undefined
age // 30
上面代码中,调用Person构造函数时,忘了加上new命令,结果,变量p变成了undefined,而age属性变成了全局变量。
3.2 new命令的原理
使用new命令时,内部将依次执行下面的步骤。
- 创建一个空对象,作为将要返回的对象实例。
- 将这个空对象的原型,指向构造函数的prototype属性。
- 将这个空对象赋值给函数内部的this关键字。
- 开始执行构造函数内部的代码。
也就是说,构造函数内部,this指向的是一个新生成的空对象,所有this操作,都发生在这个空对象上。
new命令简化的内部流程,可以用下面的代码表示。
function _new(/* 构造函数 */ constructor, /* 构造函数参数 */ params) {
// 将 arguments 对象转为数组
var args = [].slice.call(arguments);
// 取出构造函数
var constructor = args.shift();
// 创建一个空对象,继承构造函数的 prototype 属性
var context = Object.create(constructor.prototype);
// 执行构造函数
var result = constructor.apply(context, args);
// 如果返回结果是对象,就直接返回,否则返回 context 对象
return (typeof result === 'object' && result != null) ? result : context;
}
// 实例
var man1 = _new(Person, '张三', 28);
3.3 new.target
函数内部可以使用new.target属性。如果当前函数是new命令调用,new.target指向当前函数,否则为undefined。
function f() {
console.log(new.target === f);
}
f() // false
new f() // true
注:本文适用于ES5规范,原始内容来自 JavaScript 教程,有修改。