ECMAScript 5定义了一个名为Object.create()的方法,它建立一个新对象, 其中第一个参数是这个对象的原型。Object.create()提供第二个可选参数,用以对对象的属性进行进一步描述。
Object.create()是一个静态函数,而不是提供给某个对象调用的方法。使用它的方法很简单,只须传入所需的原型对象便可:javascript
var o1 = object.create({x:1, y:2}); // o1继承了属性x和y
经过原型继承建立一个新对象前端
// inherit() 返回了一个继承自原型对象p的属性的新对象 //这里使用ECMAScript 5中的0bject. create()函数(若是存在的话) //若是不存在0bject . create(),则退化使用其余方法 function inherit(p) { if (p == nul1) throw TypeError(); // p是一个对象,但不能是null if (Object.create) // 若是bject. create()存在 return object.create(p); // 直接使用它 var t = typeof p; // 不然进行进- -步检测 if (t !== "object" & t !== "function") throw TypeError(); function f() {}; // 定义一个空构造函数 f.prototype = p; // 将其原型属性设置为p return new f(); // 使用f()建立p的继承对象 }
假设要查询对象o的属性x,若是o中不存在x,那么将会继续在o的原型对象中查询属性x。若是原型对象中也没有x,但这个原型对象也有原型,那么继续在这个原型对象的原型上执行查询,直到找到x或者查找到一个原型是nul1的对象为止。能够看到,对象的原型属性构成了一个“链”,经过这个“链”能够实现属性的继承。
实例java
var o = {}; o.x = 1; var p = inherit(o); p.y = 2; var q = inherit(p); q.y = 3; var s = q.toString(); q.x + q.y // =>3
假设给对象o的属性x赋值:web
若是o中已有x属性,则需先断定x属性是o继承的属性仍是自有属性,从而进一步断定属性x是否为只读属性,若是o的原型链中存在该属性但不容许修改则会致使属性赋值失败;segmentfault
总结:属性赋值要么失败,要么建立一个属性,要么在原始对象中设置属性,不会影响到原型链。但有一个例外,若是o继承自属性x,而这个属性是一个具备setter方法的accessor属性。数组
属性访问并不老是返回或设置一个值。查询一个不存在的属性并不会报错,若是在对象o自身的属性或继承的属性中均未找到属性x,属性访问表达式o.x返回undefined。框架
这里假设book对象有属性"sub-title",而没有属性"subtitle"dom
book.subtitle; // => undefined: 属性不存在
可是,若是对象不存在,那么试图查询这个不存在的对象的属性就会报错。null和undefined值都没有属性,所以查询这些值的属性会报错,接上例:函数
//抛出一个类型错误异常,undefined没有length属性 var len = book.subtitle.length;
除非肯定book和book.subtitle都是(或在行为上)对象,不然不能这样写表达式book.subtitle.length,由于这样会报错,下面提供了两种避免出错的方法:工具
方法一
//一种冗余但很易懂的方法 var len = undefined; if (book) { if (book. subtitle) len = book.subtitle.length; }
方法二
//一种更简练的经常使用方法,获取subtitle的length属性或undefined var len = book && book.subtitle && book.subtitle.length;
这里利用了&&操做符的短路特色。
delete运算符能够删除对象的属性。它的操做数应当是一个属性访问表达式。让人感到意外的是,delete 只是断开属性和宿主对象的联系,而不会去操做属性中的属性。
delete book.author; // book再也不有属性author delete book["main title"]; // book 也再也不有属性"main title"
delete运算符只能删除自有属性,不能删除继承属性(要删除继承属性必须从定义这个属性的原型对象.上删除它,并且这会影响到全部继承自这个原型的对象)。
举例:
a = { p: { x: 1 } }; b = a.p; delete a.p; b.x //=>1,执行这段代码以后b.x的值依然是1
因为已经删除的属性的引用依然存在,所以在JavaScript的某些实现中,可能由于这种不严谨的代码而形成内存泄漏。因此在销毁对象的时候,要遍历属性中的属性,依次删除。
当delete表达式删除成功或没有任何反作用(好比删除不存在的属性)时,它返回true。若是delete后不是一个属性访问表达式,delete一样返回true:
delete不能删除那些可配置性为false的属性(尽管能够删除不可扩展对象的可配置属性)。某些内置对象的属性是不可配置的,好比经过变量声明和函数声明建立的全局对象的属性。在严格模式中,删除一个不可配置属性会报一个类型错误。
检测一个对象是不是另外一个对象的原型。或者说一个对象是否被包含在另外一个对象的原型链中
实例一:
var p = {x:1}; //定义一个原型对象 var o = Object.create(p); //使用这个原型建立一个对象 p.isPrototypeOf(o); //=>true:o继承p Object.prototype.isPrototypeOf(p); //=> true, p继承自Object.prototype
实例二:
function Animal(){ this.species = "动物"; }; var eh = new Animal(); Animal.prototype.isPrototypeOf(eh) //=>true
综合上面的两个例子,咱们发如今调用isPrototypeOf()的时候有三种方式
p.isPrototypeOf(o); //=>true Object.prototype.isPrototypeOf(p); Animal.prototype.isPrototypeOf(eh) //=>true
总结一下就是:
经过Object.create()建立的对象使用第一个参数做为原型
经过对象直接量的对象使用Object.prototype做为原型
经过new建立的对象使用构造函数的prototype属性做为原型
Instanceof运算符但愿左操做数是一个对象,右操做数标识对象的类。若是左侧对象是右侧类的实例,则表达式返回为true,不然返回false。
javascript var d = new Date(); d instanceof Date; //=>true d是Date的实例 d instanceof Object; //=>true 全部对象都是Object的实
当经过instanceof判断一个对象是不是一个类的实例的时候,这个判断也会包含对父类的检测。尽管instanceof的右操做数是构造函数,但计算过程实际是检测了对象的继承关系。
instanceOf与isPrototypeOf简单总结
var d = new Date(); Date.prototype.isPrototypeOf(d); //=>true d instanceof Date; //=>true
* 若是Date.prototype是d的原型,那么d必定是Date的实例。 * 缺点为没法同对象来得到类型,只能检测对象是否属于类名 * 在多窗口和多框架的子页面的web应用中兼容性不佳。其中一个典型表明就是instanceof操做符不能视为一个可靠的数组检测方法。
对象的hasOwnProperty()方法用来检测给定的名字是不是对象的自有属性。对于继承属性它将返回false:
var o = { x: 1 } o.hasOwnProperty("x"); // true: o有一个自有属性x o.hasOwnProperty("y"); // false: o中不存在属性y o.hasOwnProperty("toString"); // false: toString是继承属性
in运算符左侧是属性名,右侧是对象,若是对象的自有属性或者继承属性中包含这个属性则返回true。
var o = { x = 1 } "x" in o; // =>true "y" in o; // =>false "toString" in o; // =>true: toString是继承属性
只有检测到是自有属性且这个属性可枚举(enumberable attribute)为true时它才返回true。某些内置属性是不能枚举的。
var o = inherit({ y: 2 }); o.x = 1; o.propertyIsEnumerable("x"); // true: o有一个可枚举的自有属性x o.propertyIsEnumerable("y"); // false: y是继承来的 Object.prototype.propertyIsEnumerable("toString"); // false: 不可枚举
能够在循环体中遍历对象中全部可枚举的属性(包括自有属性和继承的属性),把属性名称赋值给循环变量。对象继承的内置方法不可枚举的,但在代码中给对象添加的属性都是可枚举的(除非用下文中提到的一个方法将它们转换为不可枚举的)。例如:
var o = {x:1, y:2, z:3}; //三个可枚举的自有属性 o.propertyIsEnumerable("toString") // =>false, 不可枚举 for(p in o) //遍历属性 console.log(p); //输出x、y和z,不会输出toString
有许多实用工具库给0bject.prototype添加了新的方法或属性,这些方法和属性能够被全部对象继承并使用。然而在ECMAScript 5标准以前,这些新添加的方法是不能定义为不可枚举的,所以它们均可以在for/i n循环中枚举出来。为了不这种状况,须要过滤for/in循环返回的属性,下面两种方式是最多见的:
for(p in o) { if (!o. hasOwnProperty(p)) continue; // 跳过继承的属性 } for(p in o) { if (typeof o[p] === "function") continue; // 跳过方法 }
返回一个由指定对象的全部自身属性的属性名(包括不可枚举属性但不包括Symbol值做为名称的属性)组成的数组。
var arr = ["a", "b", "c"]; console.log(Object.getOwnPropertyNames(arr).sort()); // ["0", "1", "2", "length"] // 类数组对象 var obj = { 0: "a", 1: "b", 2: "c"}; console.log(Object.getOwnPropertyNames(obj).sort()); // ["0", "1", "2"] // 使用Array.forEach输出属性名和属性值 Object.getOwnPropertyNames(obj).forEach(function(val, idx, array) { console.log(val + " -> " + obj[val]); }); // 输出 // 0 -> a // 1 -> b // 2 -> c //不可枚举属性 var my_obj = Object.create({}, { getFoo: { value: function() { return this.foo; }, enumerable: false } }); my_obj.foo = 1; console.log(Object.getOwnPropertyNames(my_obj).sort()); // ["foo", "getFoo"]
返回一个由一个给定对象的自身可枚举属性组成的数组,数组中属性名的排列顺序和使用for…in循环遍历该对象时返回的顺序一致 。若是对象的键-gs值都不可枚举,那么将返回由键组成的数组。
// simple array var arr = ['a', 'b', 'c']; console.log(Object.keys(arr)); // console: ['0', '1', '2'] // array like object var obj = { 0: 'a', 1: 'b', 2: 'c' }; console.log(Object.keys(obj)); // console: ['0', '1', '2'] // array like object with random key ordering var anObj = { 100: 'a', 2: 'b', 7: 'c' }; console.log(Object.keys(anObj)); // console: ['2', '7', '100'] // getFoo is a property which isn't enumerable var myObj = Object.create({}, { getFoo: { value: function () { return this.foo; } } }); myObj.foo = 1; console.log(Object.keys(myObj)); // console: ['foo']
咱们将存取器属性的getter和setter方法当作是属性的特性。按照这个逻辑, 咱们也能够把数据属性的值一样看作属性的特性。所以,能够认为一个属性包含一个名字和4个特性。
数据属性的4个特性分别是它的值(value) 、可写性(writable) 、可枚举性(enumerable) 和可配置性(configurable) 。
存取器属性不具备值(value) 特性和可写性,它们的可写性是由setter方法存在与否决定的。所以存取器属性的4个特性是读取(get)、写入(set)、可枚举性和可配置性。
为了实现属性特性的查询和设置操做,ECMAScript 5中定义了一个名为“属性描述符”(property descriptor)的对象,这个对象表明那4个特性。描述符对象的属性和它们所描述的属性特性是同名的。
所以,数据属性的描述符对象的属性有value、writable.enumerable和configurable。存取器属性的描述符对象则用get属性和set属性代替value和writable。其中writable、 enumerable和configurable都是布尔值,固然,get属性和set属性是函数值。
每个对象都有与之相关的原型(prototype) 、类(class) 和可扩展性(extensible attribute)。
对象序列化(serialization) 是指将对象的状态转换为字符串,也可将字符串还原为对象。ECMAScript 5提供了内置函数JSON.stringify()和JSON.parse()用来序列化和还原JavaScript对象。这些方法都使用JSON做为数据交换格式,JSON的全称是“JavaScript Object Notation" 一JavaScript对象表示法,它的语法和JavaScript对象与数组直接量的语法很是相近:
o = {x:1, y:{z:[false, null, ""]}}; //定义一个测试对象 s = JSON.stringify(o); // s是'{"x":1,"y":{"z" :[false, null, ""]}}' p = JSON.parse(s); // p是o的深拷贝
参考:
* 《JavaScript权威指南》第六版 * [MDN Web 文档](https://developer.mozilla.org/zh-CN/)
推荐阅读:
【专题:JavaScript进阶之路】
JavaScript之“use strict”
JavaScript之new运算符
JavaScript之call()理解
我是Cloudy,年轻的前端攻城狮一枚,爱专研,爱技术,爱分享。
我的笔记,整理不易,感谢阅读、点赞和收藏。
文章有任何问题欢迎你们指出,也欢迎你们一块儿交流前端各类问题!