理解原型与原型链

原型与原型链是学习JavaScript这门语言不能不理解的两个重要概念,为何?由于JavaScript是一门基于原型的语言。javascript

怎么理解“JavaScript是一门基于原型的语言”?在软件设计模式中,有一种模式叫作原型模式,JavaScript正是利用这种模式而被建立出来。先来了解下原型模式的概念:原型模式是用于建立重复的对象,同时又能保证性能。这种类型的设计模式属于建立型模式,它提供了一种建立对象的最佳方式。这种模式是实现了一个原型接口,该接口用于建立当前对象的克隆。原型模式的目的是用原型实例指定建立对象的种类,而且经过拷贝这些原型建立新的对象。也就是说利用已有的一个原型对象,能够快速地生成和原型对象同样的新对象实例。
java

原型:设计模式

原型究竟是什么?原型是一个能够被复制(或者叫克隆)的一个类,经过复制原型能够建立一个如出一辙的新对象。通俗的说,原型就是一个模板,在设计语言中更准确的说是一个对象模板数组

var Person = function(name){
    this.name = name;
}
            
Person.prototype.sayHi = function(){
    console.log("Hello! I am " + this.name + ".");
}
            
var tom = new Person("tom");
var mary = new Person("mary");
            
tom.sayHi();    //Hello! I am tom.
mary.sayHi();   //Hello! I am mary.复制代码

从上面的代码能够看出来,原型(Person)定义了一些公用的属性和方法;利用原型(Person)建立出来的新对象实例(tom和mary对象)会共享原型(Person)的全部属性和方法。bash

var Person = function(name){
    this.name = name;
}
            
Person.prototype.sayHi = function(){
    console.log("Hello! I am " + this.name + ".");
}
            
var tom = new Person("tom");
var mary = new Person("mary");
            
tom.sayHi();    //Hello! I am tom.
mary.sayHi();   //Hello! I am mary.
tom.getName = function(){
    console.log(this.name);
}
tom.getName();//tom
mary.getName();//undefined复制代码

此次新对象实例tom添加getName方法,最后只有实例tom拥有该方法,而另外一个实例mary并无getName方法,这说明:经过原型建立的新实例是相互独立的。函数

也正是由于这样对象的使用才能更加灵活、更加易于扩展。性能

原型链学习

在JavaScript中,全部的对象都拥有一个__proto__属性指向该对象的原型(prototype)。在上面的代码中,执行console.log(tom.__proto__)后在控制台能够看到输出了原型(Person),而后继续执行console.log(Person.__proto__),控制台打印的结果是function(){},为何Person的原型是function呢?这是由于Person是一个构造函数,它的本质就是一个函数。在 JavaScript中函数是一等对象。ui

如今用箭头符号来表示上面原型建立的过程:this

tom.__proto__ => Person
Person.__proto__ => function(){}复制代码

因而可知,原型链记录了原型对象建立的过程,所以,我给原型链添加一个概念:原型链是原型对象建立过程的历史记录。

var A = {
  a:1,
  b:2
};
                
var B = Object.create(A);
var C = Object.create(B);
            
B.c = 3;
C.d = 4;

console.log(C)    //{d:4}
            
for(var key in C){
  console.log(C[key])
}

//会在控制台中依次打印出4 3 1 2复制代码

注意在控制台打印的顺序是4 3 1 2,而不是1 2 3 4。为何?由于C的原型是B,B的原型是A。C对象只有一个d属性,执行for...in是先返回C.d,而后再从C的原型(B)中查找到并返回B.c,再从B的原型(A)中查找到并返回A.a和A.b,最后从A的原型(Object)中查找,发现Object中没有任何属性,因而结束for...in 。因此最后的结果4 3 1 2。这就解释了原型继承是查找属性的过程是先查找自身属性,当自身属性不存在是,会在原型链中逐渐查找。

有时只须要查找对象自身的属性,那么,这种消耗就是一种浪费,怎么解决呢?

hasOwnProperty函数

hasOwnProperty函数能够用来检查对象自身是否含有某个属性,返回值是布尔值,当属性不存在时不会向上查找对象原型链。

var A = {
  a:1,
  b:2
};
                
var B = Object.create(A);
var C = Object.create(B);
            
B.c = 3;
C.d = 4;

console.log(C)    //{d:4}
            

for(var key in C){
  console.log(C["a"]) //属性 a 是原型链上的属性, 输出 1
}

if(C.hasOwnProperty("a")){
  console.log(C["a"]) //属性 a 不是自身属性,不会执行这一步
}复制代码

hasOwnProperty函数只能检查对象是否拥有某个数学系,那如何遍历对象的自身属性?

getOwnPropertyNames 函数:

getOwnPropertyNames 函数能够获取对象全部的自身属性,返回值是由对象自身属性名称组成的数组,一样不会向上查找对象原型链。

var A = {
  a:1,
  b:2
};
                
var B = Object.create(A);
var C = Object.create(B);
            
B.c = 3;
C.d = 4;

console.log(C)    //{d:4}
            
console.log(Object.getOwnPropertyNames(C))  //输出 ["d"]复制代码

利用getOwnPropertyNames函数遍历全部对象全部自身属性,例:

var f = function(){
  var o = {a:1, b:2};
  var propertys = Object.getOwnPropertyNames(o);
  var len = propertys.length;

  for(var i = 0; i < len; i++){
    var key = propertys[i];
    console.log(o[key]);//输出 1, 2
  }
};
f();复制代码
相关文章
相关标签/搜索