对象
对象可以通过两种形式定义:声明(文字)形式和构造形式。
对象的文字语法大概是这样:
var myObj = {
key: "aaa'
};
构造形式大概是这样:
var myObj = new Object();
myObj.key = "aaa";
基本类型
• string
• number
• boolean
• null
• undefined
• object
简单基本类型(string、boolean、number、null 和 undefined)本身并不是对象
实际上,null 本身是基本类型
原理是这样的,不同的对象在底层都表示为二进制,在 JavaScript 中二进制前三位都为 0 的话会被判 断为 object 类型,null 的二进制表示是全 0,自然前三位也是 0,所以执行
typeof 时会返回“object”。
内置对象
• String
• Number
• Boolean
• Object
• Function
• Array
• Date
• RegExp
• Error
操作对象的一些方法
Object.assign()
ES6 定义了 Object.assign(..) 方法来实现浅复制。Object.assign(..) 方法的第一个参数是目标对象,之后还可以跟一个或多个源对象。它会遍历一个或多个源对象的所有可枚举(enumerable) 的自有键并把它们复制到目标对象,最后返回目标对象
Object.getOwnPropertyDescriptor()
ES5开始,所有的属性都具备了属性描述符
var myObject = { a:2
};
Object.getOwnPropertyDescriptor( myObject, "a" );
// {
// value: 2,
// writable: true,// 可写
// enumerable: true,//可枚举
// configurable: true //可配置
// }
Object.defineProperty()
给对象添加一个新属性或者修改一个已有属性(如果它是 configurable)并对特性进行设置
var myObject = {};
Object.defineProperty( myObject, "a", {
value: 2,
writable: true,
configurable: true,
enumerable: true
});
myObject.a; // 2
如果把上面的属性 configurable: false 然后调用 delete myObject.a; 是删除失败的,依然可以获取到 myObject.a,原因就是因为configurable: false
Object.preventExtensions()
如果你想禁止一个对象添加新属性并且保留已有属性,可以使用 Object.prevent Extensions(..)
var myObject = {
a:2
};
Object.preventExtensions( myObject );
myObject.b = 3;
myObject.b; // undefined
在非严格模式下,创建属性 b 会静默失败。在严格模式下,将会抛出 TypeError 错误。
Object.seal()
Object.seal(..) 会创建一个“密封”的对象,这个方法实际上会在一个现有对象上调用 Object.preventExtensions(..)并把所有现有属性标记为 configurable:false,
就是不能调用Object.defineProperty()
所以,密封之后不仅不能添加新属性,也不能重新配置或者删除任何现有属性(虽然可以 修改属性的值)。
Object.freeze()
Object.freeze(..) 会创建一个冻结对象,这个方法实际上会在一个现有对象上调用 Object.seal(..) 并把所有“数据访问”属性标记为 writable:false,这样就无法修改它们的值。
hasOwnProperty() 和 in
hasOwnProperty()这个是对象实例的方法
var myObject = {
a:2
};
myObject.hasOwnProperty( "a" );// true
("a" in myObject); // true
in操作符会检查属性是否在对象及其[[Prototype]]原型链中。hasOwnProperty(..)只会检查属性是否在myObject对象中,不会检查[[Prototype]]链。
propertyIsEnumerable()
propertyIsEnumerable()这个是对象实例的方法
会检查给定的属性名是否直接存在于对象中(而不是在原型链 上)并且满足 enumerable:true。
var myObject = {
a:2
};
myObject.propertyIsEnumerable( "a" ); // true
Object.keys()
会返回一个数组,包含所有可枚举属性,只会查找对象直接包含的属性,不会检查[[Prototype]]链。
var myObject = {
a:2
};
Object.keys( myObject ); // ["a"]
Object.getOwnPropertyNames()
会返回一个数组,包含所有属性,无论它们是否可枚举,只会查找对象直接包含的属性,不会检查[[Prototype]]链。
var myObject = {
a:2
};
Object.defineProperty(
myObject,
"b",
// 让 b 不可枚举
{ enumerable: false, value: 3 }
);
Object.getOwnPropertyNames( myObject ); // ["a","b"]
[[Put]]
给对象的属性赋值会触发 [[Put]] 来设置或者创建这个属性。但是实际情况并不完全是这样。
[[Put]] 被触发时有两种情况对象已存在这个属性,和不存在这个属性
如果已经存在这个属性,[[Put]] 算法大致会检查下面这些内容。
- 属性是否是访问描述符?如果是并且存在setter就调用setter。
- 属性的数据描述符中writable是否是false?如果是,在非严格模式下静默失败,在严格模式下抛出 TypeError 异常。
- 如果都不是,将该值设置为属性的值。
Getter和Setter
getter 是一个隐藏函数,会在获取属性值时调用。setter 也是一个隐藏 函数,会在设置属性值时调用。
给对象的一个属性定义 getter、setter 或者两者都有时,这个属性会被定义为“访问描述 符”(和“数据描述符”相对)。
var myObject = {
// 给 a 定义一个 getter
get a() {
return this._a_;
},
// 给 a 定义一个 setter
set a(val) {
this._a_ = 2; }
};
Object.defineProperty(
myObject, // 目标对象
"b", // 属性名
{ // 描述符
// 给 b 设置一个 getter
get: function(){ return 2 },
// 确保 b 会出现在对象的属性列表中
enumerable: true
})
[[Prototype]]
JavaScript 中的对象有一个特殊的 [[Prototype]] 内置属性,其实就是对于其他对象的引用。几乎所有的对象在创建时 [[Prototype]] 属性都会被赋予一个非空的值(对象的 [[Prototype]] 链接可以为空)。[[Prototype]] 引用有什么用呢?当你试图引用对象的属性时会触发,[[Get]] 操作,检查对象本身是
否有这个属性,如果有的话就使用它。如果没有,就需要使用对象的[[Prototype]]链
属性设置和屏蔽
分析myObject.foo = "bar";会有怎样的过程,
foo不在myObject中而是存在于原型链上层时 myObject.foo = "bar" 会出现的三种情况。
- 如果在
[[Prototype]]链上层存在名为foo的普通数据访问属性并且没有被标记为只读(writable:false),那就会直接在myObject中添加一个名为 foo 的新 属性,它是屏蔽属性。 - 如果在
[[Prototype]]链上层存在foo,但是它被标记为只读(writable:false),那么 无法修改已有属性或者在myObject上创建屏蔽属性。如果运行在严格模式下,代码会 抛出一个错误。否则,这条赋值语句会被忽略。总之,不会发生屏蔽。 - 如果在
[[Prototype]]链上层存在foo并且它是一个setter,那就一定会 调用这个setter。foo不会被添加到(或者说屏蔽于)myObject,也不会重新定义foo这个setter。