来自《你不知道的JavaScript》第3章对象的学习总结
<============================================================>
对象定义有两种方法:声明(文字)形式和构造形式
对象文字形式:
var myObj = {
key:value
}
构造形式:
var myObj = new Object();
myObj.key = value;
<============================================================>
简单基本类型:string、number、boolean、null、undefined、object
内置对象:String、Number、Boolean、Object、Function、Array、Date、RegExp、Error
(不同的对象在底层都表示为二进制,在JS中前三位都为0的话会被判断为object类型,null的二进制表示全是0,自然前三位都是0,所以执行typeof时会返回“object”)
<============================================================>
属性与方法:
var myObject = {
foo:function(){
console.log("foo");
};
var someFoo = myObject.foo;
someFoo; //function foo(){..}
myObject.foo;//function foo(){}
}
即使在对象一文字形式声明一个函数表达式,这个函数也不会“属于”这个对象。
<=============================================================>
数组:
var myArray = ["foo",42,"bar"];
myArray["3"] = "baz";
myArray.length;//4
myArray[3] ;//"baz"
如果试图向数组添加一个属性,但是属性名看起来像一个“数字”,那它会变成一个数值下标(所以会修改数组的内容而不是添加一个属性)
var myArray = ["foo",42,"bar"];
myArray.baz = "baz";
myArray.length;//3
myArray.baz;//"baz"
<=============================================================>
属性描述符:
- writable:决定是否可以修改属性的值
- configurable:只要属性是可配置的,就可以使用defineProperty(..)方法来修改属性描述符(即便属性是configurable:false,我们还是可以把writable的状态由true改为false,但无法由false改为true,别外,configurable:false会禁止删除这个属性)
<==============================================================>
遍历:
for..in循环可以用来遍历对象的可枚举属性列表(包括[[Prototype]]链)。
ES5增加了一组数组的辅助迭代器,包括forEach(..)、every(..)和some(..)。
forEach(..)会遍历数组中的所有值并忽略回调函数的返回值;every(..)会一直运行直到回调函数返回false(或者“假”值),some(..)会一直运行直到回调函数返回true(或者“真”值)。
<==============================================================>
存在性
in操作符会检查属性是否在对象及其[[Prototype]]原型链中,可枚举。而hasOwnProperty(..)只会检查属性是否在myObject对象中,不会检查[[Prototype]]链。
var myObject = {
a:2
}
("a" in myObject);//true
("b" in myObject);//false
myObject.hasOwnProperty("a");//true
myObject.hasOwnProperty("b");//false
看起来in操作符可以检查容器内是否有某个值,但是他实际上检查的是某个属性名是否存在。对于数组来说这个区别非常重要,4 in [2,4,6]的结果并不是true,因为[2,4,6]这个数组中包含的属性名是0、1、2,没有4。
propertyIsEnumerable(..)会检查给定的属性名是否直接存在于对象中(而不是在原型链上)并且满足enumberable:true
Object.keys(..)会返回一个数组,包含所有可枚举属性,不包括原型链上的属性。Object.getOwnPropertyNames(..)会返回一个数组,包含所有属性,无论他们是否可枚举,Object.keys(..)和Object.getOwnPropertyNames(..)都只会查找对象直接包含的属性,不包括原型链上面的。
ES6增加了一种用来遍历值而不是数组下标(或者对象属性)的for..of循环语法。
var myArray = [1,2,3];
for(var v of myArray){
console.log(v);
}
//1
//2
//3
//部分DEMO
var myArray = ['Hello', 'World!'];
myArray.bar = "nice to meet you!";
console.log(myArray.length); //2
myArray['baz'] = "Good luck to you!";
console.log(myArray.length); //2
for (var idx in myArray) {
console.log(idx, myArray[idx]);
//0 Hello
//1 World!
//bar nice to meet you!
//baz Good luck to you!
}
var myObj = {};
Object.defineProperty(myObj, "a", {
//value:2
//property descriptors must not specify a value or be writable when a getter or setter has been specified
// writable:true,
configurable: true,
enumerable: true,
get: function () {
return this._a_ * 3;
},
set: function (val) {
this._a_ = val * 2;
}
});
myObj.a = 2;
console.log(myObj.a); //12
Object.defineProperty(myObj, "b", {
writable: false, //决定是否可以修改属性的值(是否可写)
configurable: true, //只要属性是可配置的,就可以使用defineProperty(..)方法来修改属性描述符
enumerable: true,
value: 2
});
myObj.b = 3;
console.log(myObj.b); //2
Object.defineProperty(myObj, "b", {
get: function () {
return 5;
},
set: function () {
// throw new Error('this value is not writable!');
}
});
myObj.b = 3; // [显示/隐藏消息细节。] Error: this value is not writable!
console.log(myObj.b); //5
Object.defineProperty(myObj, "c", {
writable: true, //决定是否可以修改属性的值(是否可写)
configurable: false, //只要属性是可配置的,就可以使用defineProperty(..)方法来修改属性描述符
enumerable: true,
value: 4
});
console.log(myObj.c); //4
myObj.c = 5;
console.log(myObj.c); //5
delete myObj.c;
console.log('hi', myObj.c); //hi 5
//即便属性是configurable,依旧可以将writable的状态由true改为false
Object.defineProperty(myObj, "c", {
writable: false, //决定是否可以修改属性的值(是否可写)
//用户定义的所有的普通属性默认都是enumerable:true,这通常就是我们想要的。但是如果你不希望某些特殊属性
//出现在枚举中,可以将其设置为enumerable:false
configurable: false, //只要属性是可配置的,就可以使用defineProperty(..)方法来修改属性描述符
enumerable: true,//false can't redefine non-configurable property "c"
value: 4
});
console.log('------------------------------');
//不管是不是处于严格模式,尝试修改一个不可配置的属性描述符都会出错!
//所以configurable修改成false是单向操作,无法撤销,别:configurable:false会禁止删除该属性
Object.defineProperty(myObj, "c", {
writable: true, //决定是否可以修改属性的值(是否可写)
configurable: true, //只要属性是可配置的,就可以使用defineProperty(..)方法来修改属性描述符
enumerable: true,//控制的是属性是否会出现在对象的属性枚举中,比如说for..in循环。
value: 6
}); //TypeError:can't redefine non-configurable property "c"
//禁止一个对象添加新属性并且保留已有属性,可以使用Object.preventExtensions(..)
var myObject = {
a:2
};
Object.preventExtensions(myObject);
myObject.b=2;
console.log(myObject.b);//undefined
//密封Object.seal(..)会创建一个冻结对象,这个方法实际上会在一个现有对象上调用Object.preventExtensions(..)
//并把所有现有属性标记为configurable:false
var myObject1 = {
a:1,
b:2,
c:3
}
Object.seal(myObject1);
myObject1.d=4;
console.log(myObject1.d);//undeined
//冻结Object.freeze(..)会创建一个冻结对象,这个方法实际上会在一个现有对象上调用Object.seal(..)并把所有
//"数据访问"属性标记为writable:false,这样就无法修改它们的值。(不过这个对象引用的其他对象是不受影响的!)
//对象默认的[[PUT]]和[[GET]]操作分别可以控制属性值的设置和获取
//[[PUT]]被触发时,实际行为取决与诸多因素。包括对象中是否已经存在该属性(这是最重要的因素)
//如果已经存在该属性,[[PUT]]算法大致会检查下面这些内容。
//1.属性是否是访问描述符(GETTER、SETTER或两者都有)?如果是并且存在SETTER就调用SETTER
//2.属性的数据描述符中writable是否是false?如果是,在非use strict(严格模式)下静默失败,
//在严格模式下抛出TypeError异常。
//3.如果都不是,将改值修改为属性的值。
//对应访问描述符来说,javascript会忽略他们的value和writable特性,
//取而代之的是关心set和get(还有configurable和enumerable)特性。
//getter是一个隐藏函数,会在获取属性值时调用。setter也是一个隐藏函数,会在设置属性值是调用。
//无论是对象文字语法中的get a(){..},还是defineProperty的显式定义,两者都会在对象中创建一个不包含值的属性,对
//于这个属性的访问会自动调用一个隐藏函数,他的返回值会被当做属性访问的返回值。
//文字语法get a(){..}
var obj = {
//给a定义一个getter
get a() {
return 2;
}
};
//defineProperty显式定义
Object.defineProperty(
obj, //目标对象
"b", //属性名
//描述符
{
//给b设置一个getter
get: function () {
return this.a * 2
},
enumerable: true //确保b会出现在对象的属性列表中
});
obj.a; //2
obj.b; //4
//一般gettter和setter成对出现,setter会覆盖单个属性默认的[[PUT]](也成为赋值操作)操作
//访问属性时,引擎实际上会调用内部默认的[[GET]]操作,如果无法在对象本事找到需要的属性,会继续访问对象的[[Prototype]]链。