重复的对象
- 在 Javascript 中,重复执行的代码容易出现重复的对象
- 传统的构造方法的定义方式会影响性能,容易造成多个对象有多个对象副本。应该将方法单独抽取出来,让所有对象共享该方法
globalSayHello()
就是这样的一个方法。
function globalSayHello() {console.log("hello");
}
function Person() {var o = {};
o.sayHello = globalSayHello;
return o;
}
var p1 = new Person();
var p2 = new Person();
p1.sayHello == p2.sayHello; // true
针对以上情况,可以将方法全部放到外面,比如,将 sayHello()
单独定义到全局中,代码如下:
function sayHello() {//TODO}
但是这么做会有如下安全隐患
- 在开发中引入各种框架或库,自定义的成员越多,出现命名冲突的概率越大
- 可能在开发中有多个构造函数,每一个构造函数应该有多个方法,那么就会变得不容易维护
初始原型
-
任意一个对象,都会默认连接到它的原型中
- 创建一个函数,会附带创建一个特殊对象,该对象使用
函数.prototype
引用,称其为函数的原型属性。强调一下是原型属性
- 每一个由该函数作为构造函数创建的对象,都会默认连接到该对象上
- 创建一个函数,会附带创建一个特殊对象,该对象使用
function Person() {}
var p = new Person();
/* p 就是由 Person 函数作为构造函数创建出来的对象 */
/* 在 Chrome 浏览器的 watch 中可以使用如下方式查看结构 */
p._proto_ == Person.prototype // true
在原型中查找
- 在该对象访问某一个方法或属性时,如果该对象中没有,则会去到这个神秘对象中查找(也就是原型中去查找)
function Person(name) {this.name = name;}
/* 在原型中定义了 printName() 方法 */
Person.prototype.printName = function () {console.log(this.name);
};
var p = new Person("z4");
p.printName()
/**
* 构造函数 Person 中没有 printName() 这个方法
* 当 p 对象调用这个方法的时候,会先去构造方法中
* 查找,如果找不到,会去原型中查找。*/
从调试工具中可以看到,p
对象中是不存在 printName()
方法的,此方法是在 __proto__
对象中存在的
连接到原型对象(神秘对象)
- 每一个由构造函数创建出来的对象,都会默认连接到该神秘对象上
var f1 = new Foo();
var f2 = new Foo();
/* 如果 f1 中没有 sayHello 方法,就会去 Foo.prototype 中查找 */
f1.sayHello();
/* 如果 f2 中没有 sayGood 方法,就会去 Foo.prototype 中查找 */
f2.sayGood();
信息共享
- 由构造函数创建出来的众多对象共享一个对象,就是
构造函数.prototype
- 只需要将共享的东西,重复占用多的东西放到
构造函数.prototype 中
,那么所有的对象就可以共享了
var arr = [];
for(var i = 0; i < 4; i++) {arr.push({});
arr.push(function() {})
}
console.log(arr[0] == arr[2]); //false
console.log(arr[1] == arr[3]); //false
/**
* 分析:* 数组中每次新 push 的 {} 都是一个新的对象
* 所以 arr[0] == arr[2] 是 false
* 同理,每次 push 的 function(){} 也是一个新的对象,所以也不相等
*/
function createPerson() {var o = {};
o.sayHello = function () {console.log("hello");
};
return o;
}
var p1 = createPerson();
var p2 = createPerson();
console.log(p1 == p2); //false
console.log(p1.sayHello == p2.sayHello); //false
/**
* 分析:* 每次执行 createPerson() 方法时
* o 对象总是一个新的对象,所以 p1 == p2 是 false
* 同理,sayHello 方法每次也是一个新的对象
*/
现在考虑一个问题,这样创建出的对象是否很浪费资源,比如 sayHello
对象(方法也是一个对象),每次执行 createPerson()
方法都会创建一个都具有相同的功能,这样是一种资源的浪费。
解决资源浪费
function globalSayHello() {console.log("hello");
}
function createPerson() {var o = {};
o.sayHello = globalSayHello;
return o;
}
var o1 = createPerson();
var o2 = createPerson();
console.log(o1.sayHello == o2.sayHello); //true
/**
* 分析:* globalSayHello 赋值给 o.sayHello , 每次执行 createPerson() 方法时
* o.sayHello 指向的都是同一个对象,所以结果是 true,这样就解决了资源浪
* 费的问题,因为无论执行多少次 createPerson() 方法,o.sayHello 指向的
* 是同一个对象。*/
使用以上方式有个问题就是会定义很多的成员变量,每个方法都是一个成员变量。这也是在 重复对象
中提到的安全隐。可以使用原型来解决这个问题。
使用原型解决问题
function Person(name) {this.name = name;}
/* 在原型中定义了 printName() 方法 */
Person.prototype.printName = function () {console.log(this.name);
};
var p = new Person("王二麻");
var p1 = new Person("李四");
console.log(p.printName == p1.printName) //true
/**
* 无论创建多少个对象,printName 都只有一个,都是指向的同一个引用,所以
* 资源浪费的问题解决了,还有将方法定义在原型属性中,也减少了一个成员变
* 量。如果需要定义多个方法,都可以定义在原型属性中,也可以采用下面的方式
* 给原型属性赋值。*/
Person.prototype = {say: function () {return "hello";},
hobby: function () {return "javascript";}
}
var p2 = new Person("田七");
console.log(p2.say() + " " + p2.hobby()); //hello javascript
原型相关概念
-
神秘对象针对构造函数称为
原型属性
- 神秘对象就是构造函数的原型属性
- 简称原型
-
神秘对象与构造函数所创建出来的对象也有一定关系
- 神秘对象针对构造函数创建出来的对象称为
原型对象
- 简称原型
- 神秘对象针对构造函数创建出来的对象称为
-
对象继承自其原型
- 构造函数创建的对象,继承自
构造函数的原型属性
- 构造函数创建的对象,继承自
该对象的原型对象
-
构造函数创建的对象
与构造函数的原型属性表示的对象
是两个不同
的对象
- 构造函数创建的对象,继承自