从对象的创建到原型链
JavaScript的原始数据类型(primitive type)包括数字、字符串、布尔值、null和undefined(尽管typeof null为object),其他所有的值都是对象。
对象是属性的容器,其中每个属性拥有名字和值。属性的名字可以使包括空字符串在内的任意字符串。属性值可以是除undefined值之外的任何值。
JavaScript包含一种原型链的特性,允许对象继承另一个对象的属性。
对象的创建
对象字面量创建
这是一种直接创建新对象的方法。包围在一对花括号中的零或多个“名/值”对即为一个对象的字面量。
1 var creature={}; 2 var animal={ 3 "first-name":"meow", 4 sex:"male", 5 age:"12", 6 say:function () { 7 return 'I am '+animal["first-name"]; 8 } 9 } 10 console.log(animal.say()) ;//I am meow
注:
- 逗号用来分割“名/值”对。
- 属性名是合法的JavaScript标识符且不是保留字,则不强求用引号括住属性名。但是属性名中的-是不合法的,如"first-name",所以引号是必须的,如果是first_name,引号则是可选的。
- 当属性名不合法时,需要采用在[]后缀中括住一个字符串表达式的方式。如上面第7行。
- 当属性为函数时,称其为方法。如第6、10行。
create方法
每个对象都连接到一个原型对象上,并且它可从中继承属性。所有通过字面量方式穿件的对象都连接到Object.prototype, 这是JavaScript的标配对象。
通过Object.create方法,使你创建一个新对象时,可以选择某个对象作为它的原型。
1 var animal={ 2 "first-name":"meow", 3 age:"12", 4 say:function () { 5 return 'I am '+animal["first-name"]; 6 } 7 } 8 var dog=Object.create(animal); 9 dog.name="dog"; 10 dog.age=13; 11 dog.say=function () { 12 return 'I am a dog'; 13 } 14 console.log(dog) ;
其中控制台返回的dog的属性如图所示。这是一种“差异化继承”。通过定制一个新对象,我们指明它与所基于基本对象的区别。
当我们对某个对象做出改变时,不会触及该对象的原型,原型连接只有在检索的时候才会被用到。
但我们尝试获取对象的某个属性值,但该对象没有此属性名,那么JavaScript会尝试着从原型对象中获取属性值。如果原型对象也没有,则从他的原型中寻找,以此类推,直到最后到达终点Object.prototype。这就是一条原型链。如果想要的属性完全不存在与原型链中,那么结果就是undefined值。这个过程称为委托。
某属性->对象属性->原型属性->原型的原型的属性->....->Object.prototype。
构造器函数
我们还可以通过构造器函数(constructor function)的方式来创建对象。使用这种方法来创建对象时,需要使用new操作符。
当一个函数对象被创建时,Function构造器产生的函数对象会为其添加一个prototype属性,this.prototype={constructor:this}。这个prototype的值是一个包含constructor属性且属性值为该函数的对象。这个prototype对象时存放继承特征的地方。因为JavaScript没有提供一种方法确定哪个函数是用在做构造器的,所以每个函数都会得到一个prototype对象。
用构造函数创建的对象,其原型为这个构造器函数的prototype对象。
使用构造器函数的好处之一是可以再创建对象时接受一些参数。
1 //创建一个名为User的构造函数,它构造一个带有name和age属性的对象。 2 function User(name,age){ 3 this.name = name; 4 this.age = age; 5 } 6 //给User的所有实例提供一个名为getName的公共方法 7 User.prototype.getName = function(){//公有方法 8 return this.name; 9 } 10 //构造一个User实例 11 var xiaoming= new User('小明',26); 12 console.log(xiaoming);//output:如下图
注:__proto__与prototype并不等价。__proto__是某个实例对象的属性,而prototype则是属于构造器函数的属性。
注:为了避免漏掉new的情况,构造器函数的命名方式需要采用首字母大写的方式,并且这种方式只用来命名构造器函数。
原型链
原型自身具有对象固有的普遍特征,每个对象有一个指向其原型对象的链接(即__proto__),通过这个链接我们可以再新建的对象中调用相关原型对象的方法和属性,由此形成一条链,就是原型链。
原型链的形成是靠__proto__, 而非prototype。如下所示。引自JavaScript原型及原型链详解
1 var animal = function(){}; 2 var dog = function(){}; 3 animal.price = 2000; 4 dog.prototype = animal; 5 var tidy = new dog(); 6 console.log(dog.price) //undefined 7 console.log(tidy.price) // 2000
执行dog.price的时候,发现没有price这个属性,虽然prototype指向的animal有这个属性,但它并没有去沿着这个“链”去寻找。同样,执行tidy.price的时候,也没有这个属性,但是__proto__指向了animal,它会沿着这个链去寻找,animal中有price属性,所以tidy.price输出2000。由此得出,原型链的真正形成是靠的__proto__,而不是prototype。
参考文献:
《JavaScript语言精粹》
《JavaScript面向对象编程指南》