《Javascript高级程序设计》读书笔记——构造函数与原型
构造函数与原型
构造函数模式
最简单的构造函数:
function Person(name, age, job) {
this.name = name;
this.age = age;
this.job = job;
this.sayName = function() {
console.log(this.name);
}
}
var p1 = new Person("jerry", 18, "Coder");
var p2 = new Person("tom", 28, "Javaer");
要创建Person的实例,需要使用new关键字。以这种方式调用函数能够创建实例对象是因为使用new后会隐式地执行以下四个步骤:
- 创建一个新对象(基于Object的对象)
- 将构造函数的作用域赋给新对象(此时this就指向了新对象)
- 执行构造函数内的代码
- 返回对象
若不使用new关键字而直接调用该函数,那它就跟普通函数一样;函数中的this就会是当前的全局作用域:
Person("jerry", 18, "Javaer"); //添加到window中
window.sayName(); // "jerry"
实例对象都会有个constructor(实际上这个属性应该是在原型上的。下一节讲原型的概念)属性,指向构造函数:
console.log(p1.constructor == Person); //true
但不建议使用这种方式来检测对象类型,因为该属性是可写的;应使用instanceof。
原型模式
前面说的最基本的构造函数模式有个很明显的问题就是:对于每次新创建一个实例时,都需要在实例上重新创建一个sayName函数;这样创建许多相同功能的函数对象显然是没有必要的。下面说的原型模式可以解决这种问题。
在JS中创建的每一个函数类型都有一个prototype(原型)属性,该属性指向一个对象 即原型对象,而这个对象的用途是可以包含由某一类型的所有实例共享的属性和方法。
通过这个例子来辅助理解下原型的用途:
function Person() {
}
Person.prototype.name = "jerry";
Person.prototype.age = 18;
Person.prototype.job = "Javaer";
Person.prototype.sayName = function() {
console.log(this.name);
}
var p1 = new Person();
var p2 = new Person();
p1.sayName() //"jerry"
console.log(p1.sayName == p2.sayName) //true
通过在原型中定义变量和函数,使得该构造函数的所有实例都能共享相同的变量和函数。这是由原型搜索机制所完成的:因为每个实例对象都有个内部属性[[prototype]]指向原型对象;当搜索对象的某个属性时,会先搜索实例对象本身,如果有则返回;没有的话就会到[[prototype]]指针指向的原型中继续寻找。 因此,在实例对象中可以覆盖原型中的同名属性。 而且,所有实例对象指向的都是同一个原型,这也就是为什么上面代码中实例对象都有相同的变量和函数。
需要注意一点,实例对象的[[prototype]]是在创建对象初由构造函数的prototype属性赋值来的;所以 如果在创建完实例对象后重写原型的话,重写后的原型是与先前创建的实例对象无关的(实例对象仍指向原来的那个原型):
function Person() {
}
Person.prototype = {
constructor: Person,
name : "Tom"
}
var p = new Person();
console.log(p.name); //"Tom"
Person.prototype = {
constructor: Person,
name: "jerry",
age: 18,
job: "Javaer",
sayName: function() {
console.log(this.name);
}
}
console.log(p.name); //"Tom"
p.sayName(); //throw error
寄生构造函数模式
名字起的有些奇怪,其实就是指封装显式创建对象的过程,然后再返回新创建的对象。
function Person() {
var o = new Object();
o.name = "Java";
o.f = function() {
console.log("Kotlin");
}
return o;
}
var temp = new Person();
console.log(temp.name); //"Java"
temp.f(); //"Kotlin"
这里使不使用new操作符都没有影响,两者效果是一样的。使用了new,构造函数在无返回值的情况下,默认返回隐式创建的新实例对象。而在构造函数中添加return语句,可以重写调用构造函数时返回的值。
这种模式本质上讲就是用个普通函数 在其中对已有对象进行添加或重写属性 以达到增强已有对象的功能;还有点类似于继承的作用。
创建一个自定义的Array对象:
function myArray() {
var arr = new Array();
arr.push.apply(arr, arguments);
arr.toString = function() {
return this.join("|");
}
return arr;
}
var colors = myArray("red", "blue", "yellow");
console.log(colors.toString()); //"red|blue|yellow"
稳妥构造函数模式
这一节的内容在红宝书上放的位置感觉是不太合适的=_= ... 对于初学者看这节的内容可能会有点迷,而且书上给的例子的代码也不全。 书上阐述的这种模式的作用 主要就是为了构造没有公共属性的对象,即模拟 私有变量,仅能通过函数访问。 建议可以跳过这一节的内容,在之后明白了闭包的概念后可以再回来看下这节内容。