构造函数简称构造器,帮助生成对象。相当于其他语言中的类。
构造函数是通过new操作符调用的函数。
按照约定,构造函数的名字是以大写字母开头。
function Person(name) {
this.name = name;
}
Person.prototype.sayHello = function() {
console.log('Hello, '+ this.name);
};
var lin = new Person('lin');
lin.sayHello(); //Hello, lin
我们可以把Person看做普通函数。它只有通过new调用时,草变成构造函数。new操作符执行步骤如下:
- 首先设置行为:创建一个新的对象,其原型为Person.prototype。
- 然后设置数据:Person接受对象作为隐式参数this,并添加实例属性。
1.JS中new操作符的实现
手动实现new操作符,实现大致如下:
// 手动实现new操作符
function newOperator(Constr, args) {
var thisValue = Object.create(Constr.prototype);
var result = Constr.apply(thisValue, args);
// new的另一个特性:可以从构造函数返回一个任意的对象
// 如果构造函数有它自身的返回值,切为对象的话,可以直接返回,如果为其他的话返回新的原型对象
if (typeof result === 'object' && result !== null) {
return result;
}
return thisValue;
}
var run = newOperator(Person, ['Run']);
run.sayHello(); //Hello, Run
2.术语:两个原型
不幸的是,在JS的术语,prototype使用意义模糊不清:
- 原型一:原型关系
一个对象可以是另一个对象的原型:
> var proto = {};
> var obj = Object.create(proto);
> Object.getPrototypeOf(obj) === proto
true
这里的例子,proto是obj的原型
- 原型二:prototype属性的值
每个构造函数C都有一个prototype属性,它指向一个对象。该对象成为构造函数C的所有实例的原型:
> function C() {}
> Object.getPrototypeOf(new C()) === C.prototype
true
因此,我们称后者(每一个构造函数特有的prototype属性)为实例原型更为贴切。
3.实例的constructor属性
默认每个函数C都包含一个C.prototype的实例原型对象,它的constructor属性指回C:
> function C() {}
> C.prototype.constructor === C
true
因为每个实例都从原型实例中继承了属性,也包括constructor属性,所以可以使用它得到实例的构造函数:
> function C() {}
> var o = new C()
> o.constructor === C
true
- constructor属性的用例
- 切换对象的构造函数
- 确定对象的构造函数名
o.constructor.name
- 创建相似对象
> function C() {}; > var a = new C(); > var b = new a.constructor(); console.log(b instanceof C); //true
- 指向父构造函数(superconstructor)
最佳实践:
每个函数function f() {}都有一个f.prototype对象,应该避免修改这个对象,而是只为它增加属性:
C.prototype.sayHello = function (...) { ... }
如果想替换C.prototype的对象,则应该手动把正确的赋值给constructor:
C.prototype = {
constructor: C,
sayHello: function (...) { ... }
}
4.instanceof运算符
value instanceof Constr
它是检验Constr.prototype是否在value的原型链上。所以和下面的等价:
Constr.prototype.isPrototypeOf(value)
所以基本值对于instanceof总是false(基本值:波尔、字符串、数字、null、undefined)
- 大多数对象都是Object的实例,但除了原型链的末端。eg:Object.prototype、Object.create(null)
- 缺陷:跨域
在Web中,每一个帧和窗口都拥有自己的域,具有独立的全局变量。对于跨域的对象,不可使用instanceof。例如:
if (myvar instanceof Array) ... //Doesn't always work
原因是如果myvar是来源于另外一个域,它就属于另外一个域的Array,而非当前域的Array。(可以用ES5的Array.isArray)
解决方法有:
1.避免跨域:浏览器有postMessage()方法,它可以把对象复制到另外一个域,而非传递引用。
2.检测实例构造函数的名字
3.使用prototype属性标记实例属于的类型T。