Javascript原型一

5次阅读

共计 3010 个字符,预计需要花费 8 分钟才能阅读完成。

重复的对象

  • 在 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

原型相关概念

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

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

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

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