-
Notifications
You must be signed in to change notification settings - Fork 0
第六章:对象
####1. 原型式继承
是javascript的核心特征
####2. javascript的对象是动态的---可以增加属性,也可以删除属性
####3. 对象最常见的用法是创建(create)、设置(set)、查找(query)、删除(delete)、检测(test)和枚举(enumerate)它的属性
####4. 对象的属性特性
- 是否可写
- 是否可枚举
- 是否可配置
####5. 三类对象和两类属性 三类对象
1. 内置对象
2. 宿主对象
3. 自定义对象
两类属性
1. 自有属性
2. 继承属性
####6. 对象的属性名可以是javascript标识符也可以是字符串直接量(包括空字符串)属性名里有连字符必须用字符串。 例: "sub-title"
####7. 对象直接量是一个表达式,这个表达式每次运算都创建并初始化一个新对象,每次计算对象直接量时,也都会计算它的每个属性的值。
####8. 对象创建的三种方式
- 对象直接量
- 关键字
new
与构造函数结合 - Object.create()
####9. 除null外,每个对象都从原型继承属性
####10. 所有通过对象直接量创建的对象都具有同一个原型对象,并可以通过Object.prototype获得对原型对象的引用
####11. new
关键字和构造函数调用创建的对象的原型就是构造函数的prototype属性的值
例: new Object() 和 {} 创建的对象均继承自 Object.prototype;
new Array()的原型是Array.prototype;
new Date()的原型是Date.prototype;
####12. 没有原型的对象为数不多, Object.prototype就是其中之一
####13. 所有的内置构造函数,以及大部分自定义的构造函数都具有一个继承自Object.prototype的原型
####14. Object.create() 是一个静态函数,用于创建一个新对象,参数就是要创建对象的原型 例: var p = Object.create({x:1,y:2}); //p继承了属性 x、y p.x //1 p.y //2 p的原型就是{x:1,y:2}
创建没有原型的对象
var q = Object.create(null); //q不继承任何属性和方法
创建普通的空对象
{} 或 new Object()
var r = Object.create(Object.prototype);
r和{}或new Object()创建的对象一样原型均为 Object.prototype
####15. 通过原型继承来的属性值也是可修改的 例: var a = {x:1, y:2}; var b = Object.create(a); b.x //1 b.y //2 //修改属性值 b.x = 3; b.y = 4; a.x //1 a.y //2 b.x //3 b.y //4
注:实际是同名覆盖已继承属性
####16. 字符串是动态的,而标识符是静态的。
####17. 对象的属性访问方式有对象.标识符
和 对象[字符串]
字符串是属性名的字符串形式。其中[字符串]可用于访问动态属性名,即运行中设定的属性名
例: function addProduct(product, name, value){
product[name] = value;
}
//对象product的属性就是动态的
//计算product的总价
function getTotal(product){
var total = 0.0;
for(var name in product){
total += product[name];
}
return total;
}
####18. 如果对象的属性名是保留字,ES3访问方式是[]
,ES5可用.
访问
通过原型继承创建一个新对象
function inherit(p){
if(p==null) throw TypeError(); //p是对象,不能为null
if(Object.create()){ //如果存在 Object.create()
return Object.create(p); //直接使用
}
var t = typeof p;
if(t!=="object" && t!=="function") throw TypeError();
function f(){}; //定义空构造函数
f.prototype = p; //将f的原型属性设置为p
return new f(); //用f()创建继承p的对象
}
####19. 对象的属性查询是依原型链进行的,对象自身没有该属性,就在查原型,原型没有,查原型的原型,直到原型为null止
####20. 给对象p的属性x赋值,
- 若p中已有属性x,则只是改变x的值
- 若p中没有属性x,则为p添加属性x并赋值
- 若p继承了x(即p的原型中有x属性),则继承的属性被新创建的同名属性覆盖
####21. 属性要么赋值失败,要么创建一个属性,要么在原始对象中设置属性,javascript中只有在查询时,才能体会到继承的存在,而设置属性则和继承无关 属性安全访问
var book = {};
var b = book && book.name && book.name.length;
b的值为undefined
var person = {name: "mlx"};
var p = person && person.name && person.name.length
p的值为3
####22. 以下场景为对象p设置属性x会失败
- p中属性x是只读。(defineProperty()方法中有一个例外,可以对可配置的只读属性重新赋值)
- p中属性x是继承的且是只读的
- p中不存在属性x,且p是不可扩展
####23. delete
运算符可以删除对象的属性,其运算结果只是断开属性和宿主对象的联系,而不会操作属性中的属性
####24. delete
运算符只能删除自有属性,不能删除继承属性
例: p = {x: 1};
delete p.x; //删除属性x,返回true
delete p.x; //什么也没做 (x不存在),返回true
delete p.toString; //什么也没做(toString是继承来的)
delete 1; //无意义 返回true
####25. delete
无法删除可配置属性为false的属性
例: delete Object.prototype; //不能删除,该属性不可配置
var x=1;
delete this.x //不能删除var声明的属性
function f(){} //声明一个全局函数
delete this.f //也不能删除全局函数
####26. in
运算符用于检测对象中是否存在某个属性(包括自有属性和继承属性)
例: var o = {x:1};
"x" in o; //true "x"是o的属性
"y" in o; //false "y"不是o的属性
"toString" in o; //true "toString"是o继承属性
####27. hasOwnProperty()
方法,用于检测给定的名称是否是对象的自有属性,对于继承属性返回false
例: var p = {x:1};
p.hasOwnProperty("x"); //true 属性x是对象p的自有属性
p.hasOwnProperty("y"); //false 对象p中不存在属性y
p.hasOwnProperty("toString") //false toString是继承的属性
####28. propertyIsEnumerable()是hasOwnProperty()的增强版,只有检测到这个属性是自有属性,且是可枚举的时返回true 通常由javascript创建的对象属性都是可枚举的 例: var p = inherit({y:2}); p.x = 1; p.propertyIsEnumerable("x"); //true, p有可枚举属性x p.propertyIsEnumerable("y"); //false, y是继承的属性 Object.prototype.propertyIsEnumerable("toString"); //false, toString不可枚举
####29. 用!==
也可判断一个属性是否是undefined(与in
运算符效果类似,但无法判断值为undefined的属性)
例: var p = {x:1};
p.x!==undefined; //true,p中有属性x
p.y!==undefined; //false, p中不存在属性y
p.toString!==undefined; //true, p继承了toString
####30. in
运算符能区分不存在的属性 和 存在但值为undefined的属性
例: var a = {x: undefined}; //x被显式的赋值为undefined
a.x !== undefined; //false, 属性存在但值为undefined
a.y !== undefined; //false, 属性不存在
"x" in a; //true, 属性存在
"y" in a; //false, 属性不存在
delete a.x //删除属性x
"x" in a; //false, 属性不存在
####31. for/in
循环可以在循环体中遍历对象中所有可枚举的属性(包括自有属性和继承的属性)
####32. Object.keys()
返回一个数组,该数组由对象中的可枚举的自有属性的名称组成
####33. Object.getOwnPropertyNames()
返回对象所有自有属性的名称,而不仅仅是可枚举属性
####34. 示例
示例一
/*
*把p中可枚举的属性复制到o中,并返回o
*如果o和p中含有同名属性,则覆盖o中的属性
*这个函数并不处理getter和setter以及复制属性
*/
function extend(o,p){
for(prop in p){ //遍历p中的所有属性
o[prop] = p[prop]; //将属性添加至o中
}
return o;
}
示例二
/*
*把p中可枚举的属性复制到o中,并返回o
*如果o和p中含有同名属性,o中的属性将不受影响
*这个函数并不处理getter和setter以及复制属性
*/
function merge(o,p){
for(prop in p){ //遍历p中的属性
if(o.hasOwnProperty[prop]) continue; //过滤掉已经在o中存在的属性
o[prop] = p[prop]; //将属性添加至o中
}
return o;
}
示例三
/*
*如果o中的属性在p中没有同名属性,则从o中删除这个属性
*返回o
*/
function restrict(o,p){
for(prop in o){ //遍历o中所有属性
if(!(prop in p)) delete o[prop]; //若在p中不存在,则删除之
}
return o;
}
示例四
/*
*如果o中的属性在p中存在同名属性,则从o中删除这个属性
*返回o
*/
function subtract(o,p){
for(prop in p){ //遍历p中的所有属性
delete o[prop]; //从o中删除(非严格模式,删除不存在属性不会报错)
}
}
示例五
/*
*返回一个新对象,这个对象同时拥有o的属性和p的属性
*如果o和p中有重名属性,使用p中的属性值
*/
function union(o,p){ return extend(extend({},o),p); }
示例六
/*
*返回一个新对象,这个对象拥有同时在o和p中出现的属性
*很像求o和p的交集,但p中属性的值被忽略
*/
function intersection(o,p){ retrun restrict(extend({},o),p); }
示例七
/*
*返回一个数组,这个数组包含的是o中可枚举的自有属性的名称
*/
function keys(o){
if(typeof o !== "object") throw TypeError(); //参数必须是对象
var result = []; //将要返回的数组
for(var prop in o){ //遍历所有可枚举的属性
if(o.hasOwnProperty(prop)){ //判断是否是自有属性
result.push(prop) //将属性添加至数组中
}
}
return result; //返回数组
}
####35. 在ES5中属性值可以用一个或两个方法代替,getter
或setter
,由getter
和setter
定义的属性称为存取器属性
####36. 存取器属性不具有可写性,因为其是否可写取决于有没有setter方法 例: var p = { //x和y是普通可读写属性 x:1, y:1, //r是可读写的存取器属性,它有getter和setter方法 get r(){ return Math.sqrt(this.x * this.x + this.y * this.y); }, set r(newValue){ var oldValue = Math.sqrt(this.x * this.x + this.y * this.y); var ratio = newValue / oldValue; this.x *= ratio; this.y *= ratio; }, //theta是只读存取器属性,它只有getter方法 get theta(){ return Math.atan2(this.y, this.x); } }
例2: var q = inherit(p); //创建一个继承getter和setter的新对象
q.x=1,q.y=1; //给q添加两个属性
console.log(q.r); //可以使用继承的存取器属性
console.log(q.theta);
####37. 一个属性包含一个名字和四个特性,其中属性分为数据属性和存取器属性
- 数据属性的四个特性分为 值(value),可写性(writable),可枚举(enumerable),可配置(configurable)
- 存取器属性不具有值特性和可写性,其可写性是由setter方法存在与否决定(get,set,enumerable,configurable)
####38. 属性描述符对象的四个属性,表示属性的特性
- 数据属性 (value,writable,enumerable,configurable)
- 存取器属性(get,set,enumerable,configurable)
其中
writable
,enumerable
,configurable
为布尔值
####39. Object.getOwnPropertyDescriptor()可获得某个对象特定属性的属性描述 例: Object.getOwnPropertyDescriptor({x:1}, "x"); //属性名用字符串形式 //返回 {value: 1,writable: true,enumerable: true, configurable: true} 查询上文对象p的theta属性 Object.getOwnPropertyDescriptor(p,"theta"); //返回 {get: /func/, set:undefined, enumerable: true, configurable: true}
//对于继承属性和不存在的属性返回 undefined
Object.getOwnPropertyDescriptor({},"x"); //undefined, 属性不存在
Object.getOwnPropertyDescriptor({},"toString"); //undefined, 继承属性
####40. 设置属性特性,或让新建属性只拥有某种特性,调用方法Object.defineProperty() 例: var o = {}; //创建一个空对象 //添加一个不可枚举属性x,并赋值为 1 Object.defineProperty(o, "x",{value: 1, writable: true, enumable: false, configurable: true}); //属性x是存在的,但不可枚举 o.x // 1 Object.keys(o); // [] //现在对属性x做修改,使其变为只读 Object.defineProperty(o, "x", {writable: false}); //试图给x赋值 o.x = 3; //操作失败,在严格模式中抛出类型错误异常 o.x; // 1 //属性依然是可配的,可通过以下方式对其值进行修改 Object.defineProperty(o, "x", {value: 2}); o.x; //2 //将x由数据属性修改为存取器属性 Object.defineProperty(o, "x", {get: function(){return 0;}}); o.x; //0
####41. Object.defineProperty()要么修改已有属性,要么新建自有属性,但不能修改继承属性
####42. 同时修改或新建多个属性,用Object.defineProperties() 第一个参数为要修改的对象,第二个参数为映射表(它包含要新建的或修改的属性名称以及他们的属性描述) 例: var p = Object.defineProperties({},{ x: {value: 1, writable: true, enumerable: true, configurable: true}, y: {value: 1, writable: true, enumerable: true, configurable: true}, r: { get: function(){return Math.sqrt(this.x * this.x + this.y * this.y)}, enumerable: true, configurable:true } }); 返回修改后的对象
####43. 任何对Object.defineProperty()
或Object.defineProperties()
违反规则的使用都会抛出类型错误异常
- 对象不可扩展,则不能添加新属性,但可编辑已有属性
- 属性不可配置,则不能修改其可配置性和可枚举性
- 存取器属性不可配置,则不能修改其getter和setter方法,也不能将其转化为数据属性
- 如果数据属性不可配置,则不能将其转化为存取器属性
- 如果数据属性是不可配置的,则不能将其可写性从
false
修改为true
,但可以从true
修改为false
- 如果数据属性不可配置,不可写,则不能修改其值;然而可配置,不可写的数据属性的值是可以修改的(实际是先转化为可写,修改值,再转化为不可写)
####44. 每个对象都有与之相关的三个属性: 原型(prototype)、类(class)、可扩展性(extensible attribute)
####45. ES5中,将对象作为参数传入 Object.getPrototypeOf() 可以查询参数的原型
####46. isPrototypeOf()
检测一个对象是否是另一个对象的原型
例: p.isPrototypeOf(o); //检测p是否是o的原型
####47. 通过对象直接量,Object.create() 创建的对象的类属性依然是 Object
,对于自定义构造函数创建的对象类属性也是Object
。内置构造函数或宿主对象均包含有意义的类属性
例: function classof(o){
if(o===null) return "Null";
if(o===undefined) return "Undefined";
return Object.prototype.toString.call(o).slice(8,-1);
}
classof(null) //"Null"
classof(1) //"Number"
classof("") //"String"
classof("false") //"Boolean"
classof({}) //"Object"
classof([]) //"Array"
classof(/./) //"Regexp"
classof(new Date()) //"Date"
classof(window) //"Window"(客户端宿主对象)
function f(){}
classof(new f()) //"Object"
####48. 对象的可扩展性是指对象是否可以添加新属性
####49. 所有内置对象和自定义对象都是显式可扩展的
####50. Object.isExtensible()
用以检测对象是否可扩展;Object.preventExtensions()
用以将对象设置为不可扩展
注意: 对象设置为不可扩展是不可逆的,即一旦设置为不可扩展将无法设置为可扩展,但
Object.preventExtensions
只影响对象本身的可扩展性,若给不可扩展对象的原型添加新属性,这个不可扩展的对象同样会继承这些新属性
####51. Object.seal()
设置对象为不可扩展,且对象所有自有属性不可配置,且不可逆;Object.isSealed()
检测对象是否封闭
####52. Object.freeze()
将对象设置为不可扩展,将其属性设置为不可配置,还将对象的自有属性设置为只读(存取器属性不受影响);Object.isFrozen()
检测对象是否被冻结
by @zhulinpinyu 👍