1

重复的对象

  • 在 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__ 对象中存在的
clipboard.png

连接到原型对象(神秘对象)

  • 每一个由构造函数创建出来的对象,都会默认连接到该神秘对象上
    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

原型相关概念

  • 神秘对象针对构造函数称为原型属性

    • 神秘对象就是构造函数的原型属性
    • 简称原型
  • 神秘对象与构造函数所创建出来的对象也有一定关系

    • 神秘对象针对构造函数创建出来的对象称为原型对象
    • 简称原型
  • 对象继承自其原型

    • 构造函数创建的对象,继承自构造函数的原型属性
    • 构造函数创建的对象,继承自该对象的原型对象
    • 构造函数创建的对象构造函数的原型属性表示的对象是两个不同的对象

blob
45 声望2 粉丝

A man who concentrates on coding.