Object() 函数
Object 本身是一个函数,用来将任意值转为对象。
如果参数为空(或者为 undefined 和 null),Object() 返回一个空对象。
var obj = Object();
// 等同于
var obj = Object(undefined);
var obj = Object(null);
obj instanceof Object // true
如果参数是原始类型的值,Object 方法将其转为对应的包装对象的实例。
var obj = Object(1);
obj instanceof Object // true
obj instanceof Number // true
var obj = Object('foo');
obj instanceof Object // true
obj instanceof String // true
var obj = Object(true);
obj instanceof Object // true
obj instanceof Boolean // true
如果 Object 方法的参数是一个对象,它总是返回该对象,即不用转换。
var arr = [];
var obj = Object(arr); // 返回原数组
obj === arr // true
var value = {};
var obj = Object(value) // 返回原对象
obj === value // true
var fn = function () {};
var obj = Object(fn); // 返回原函数
obj === fn // true
因此,可以写一个判断变量是否为对象的函数。这个方法常用于保证某个值一定是对象。
function isObject(value) {
return value === Object(value);
}
isObject([]) // true
isObject(true) // false
构造函数 new Object()
Object 构造函数的首要用途,是直接通过它来生成新对象。
var obj = new Object();
// 等同于var obj = {}
new Object() 构造函数与 Object() 的用法很相似,几乎一模一样。使用时,可以接受一个参数,如果该参数是一个对象,则直接返回这个对象;如果是一个原始类型的值,则返回该值对应的包装对象。
var o1 = {a: 1};
var o2 = new Object(o1);
o1 === o2 // true
var obj = new Object(123);
obj instanceof Number // true
两者区别是语义不同。
Object(value) 表示将 value 转成一个对象,new Object(value) 则表示新生成一个对象,它的值是 value。
Object.getPrototypeOf()
Object.getPrototypeOf() 方法返回参数对象的原型。这是获取原型对象的标准方法。
var F = function () {};
var f = new F();
Object.getPrototypeOf(f) === F.prototype // true
Object.setPrototypeOf()
Object.setPrototypeOf() 方法为参数对象设置原型,返回该参数对象。它接受两个参数,第一个是现有对象,第二个是原型对象。
var a = {};var b = {x: 1};Object.setPrototypeOf(a, b);
Object.getPrototypeOf(a) === b // true
a.x // 1
new 命令可以使用 Object.setPrototypeOf() 方法模拟。
var F = function () { this.foo = 'bar'; };
var f = new F();// 等同于
var f = Object.setPrototypeOf({}, F.prototype);
F.call(f);
Object.prototype.proto
实例对象的 proto 属性,返回该对象的原型。该属性可读写。
var obj = {};var p = {};
Object.create()
Object.create() 方法接受一个对象作为参数,目的是以参数对象为原型,返回一个实例对象。该实例完全继承原型对象的属性。
很多时候,需要从一个实例对象 A 生成另一个实例对象 B,如果 A 是由构造函数创建的,那么可以很轻松的得到 A 的构造函数重新生成实例 B,然而很多时候,A 只是一个普通的对象,并不是由构造函数生成的,这时候就需要使用Object.create() 方法由 A 生成 B。
var A = {
print: function () {
console.log('hello');
}
};
var B = Object.create(A);
Object.getPrototypeOf(B) === A // true
B.print() // hello
B.print === A.print // true
Object.create() 方法兼容性处理,即生成实例的本质:
if (typeof Object.create !== 'function') {
Object.create = function (obj) {
function F() {} // 新建一个空的构造函数 F
F.prototype = obj; // 让 F.prototype 属性指向参数对象 objreturn new F(); // 最后返回一个 F 的实例
};
}
下面三种方式生成的新对象是等价的:
var obj1 = Object.create({});
var obj2 = Object.create(Object.prototype);
var obj3 = new Object();
如果想要生成一个不继承任何属性(比如没有 toString 和 valueOf 方法)的对象,可以将 Object.create 的参数设为 null。因为生成的实例对象原型是 null,所以它就不具备定义在 Object.prototype 原型上面的方法。
var obj = Object.create(null);
Object.create() 方法还可以接受第二个参数。该参数是一个属性描述对象,它所描述的对象属性,会添加到实例对象,作为该对象自身的属性。
var obj = Object.create({}, {
p1: {
value: 123,
enumerable: true,
configurable: true,
writable: true,
},
p2: {
value: 'abc',
enumerable: true,
configurable: true,
writable: true,
}
});
// 等同于var obj = Object.create({});
obj.p1 = 123;
obj.p2 = 'abc';
Object.create() 方法生成的对象,继承了它的原型对象的构造函数。
function A() {}
var a = new A();
var b = Object.create(a);
b.constructor === A // true
b instanceof A // true
思考:
Object.create()和new Object的区别?
var person = {
name:"a",
sayName:function(){
console.log(this.name)
}
}
var myPerson1 = Object.create(person);
myPerson1.sayName();
var myPerson2 = Object.create(person,{name:{value:'b'}});
myPerson2.name = 'c'
console.log(myPerson2.name)
Object.create vs new
Object.create与new区别:
1.Object.create传入对象将作为新建对象的原型。
2.Object.create传入构造函数原型时,无论构造函数是否返回对象,Object.create只会return 以构造函数原型来新建的对象(参见下面的polyfill),而new则参见第4點
check this:
function Dog(name){
const _name = name;
function getName(){
return _name;
}
return {
name : getName
run : function(){}
};
}
Dog.prototype.setName = function(name){
this.name = name;
};
const pet1 = new Dog('rocky');
console.log(pet1.name);
pet1.setName('skye');
pet1.run();
const pet2 = Object.create(Dog.prototype);
pet2.setName('skye');
console.log(pet2.name);
pet2.run();
check polyfill from MDN:
if (typeof Object.create !== "function") {
Object.create = function (proto, propertiesObject) {
if (typeof proto !== 'object' && typeof proto !== 'function') {
throw new TypeError('Object prototype may only be an Object: ' + proto);
} else if (proto === null) {
throw new Error("This browser's implementation of Object.create is a shim and doesn't support 'null' as the first argument.");
}
if (typeof propertiesObject != 'undefined') throw new Error("This browser's implementation of Object.create is a shim and doesn't support a second argument.");
function F() {}
F.prototype = proto;
return new F();
};
分类
Object() 函数 Object 本身是一个函数,用来将任意值转为对象。 如果参数为空(或者为 undefined 和 null),Object() 返回一个空对象。 var obj = Object(); // 等同于 var obj = Object(undefined); var obj = Object(null);
obj instanceof Object // true 如果参数是原始类型的值,Object 方法将其转为对应的包装对象的实例。 var obj = Object(1); obj instanceof Object // true obj instanceof Number // true var obj = Object('foo'); obj instanceof Object // true obj instanceof String // true var obj = Object(true); obj instanceof Object // true obj instanceof Boolean // true 如果 Object 方法的参数是一个对象,它总是返回该对象,即不用转换。 var arr = []; var obj = Object(arr); // 返回原数组 obj === arr // true var value = {}; var obj = Object(value) // 返回原对象 obj === value // true var fn = function () {}; var obj = Object(fn); // 返回原函数 obj === fn // true 因此,可以写一个判断变量是否为对象的函数。这个方法常用于保证某个值一定是对象。 function isObject(value) { return value === Object(value); }
isObject([]) // true isObject(true) // false
构造函数 new Object() Object 构造函数的首要用途,是直接通过它来生成新对象。 var obj = new Object(); // 等同于var obj = {} new Object() 构造函数与 Object() 的用法很相似,几乎一模一样。使用时,可以接受一个参数,如果该参数是一个对象,则直接返回这个对象;如果是一个原始类型的值,则返回该值对应的包装对象。 var o1 = {a: 1}; var o2 = new Object(o1); o1 === o2 // true var obj = new Object(123); obj instanceof Number // true 两者区别是语义不同。 Object(value) 表示将 value 转成一个对象,new Object(value) 则表示新生成一个对象,它的值是 value。
思考题 [] + {}; {} + []; {} + {}; [] + [];
[] + {}; // "[object Object]" {} + []; // 0 你不知道的 JavaScript(中卷)第五章P102原文: 表面上看+ 运算符根据第一个操作数([] 或{})的不同会产生不同的结果,实则不然。 第一行代码中,{} 出现在+ 运算符表达式中,因此它被当作一个值(空对象)来处理。第 4 章讲过[] 会被强制类型转换为"",而{} 会被强制类型转换为"[object Object]"。 但在第二行代码中,{} 被当作一个独立的空代码块(不执行任何操作)。代码块结尾不需 要分号,所以这里不存在语法上的问题。最后+ [] 将[] 显式强制类型转换(参见第4 章) 为0。
Object 对象的原生方法 Object 对象的原生方法分成两类:Object 本身的方法与Object 的实例方法。 (1) 本身的方法 本身的方法就是直接定义在 Object 对象的方法。
Object.keys(obj) // ["p1"] Object.getOwnPropertyNames 方法与 Object.keys 类似,也是接受一个对象作为参数,返回一个数组,该数组的成员是参数对象自身的(非继承的)全部属性的属性名,不管该属性是否可枚举。 var a = ['Hello', 'World']; Object.keys(a) // ["0", "1"]
Object.getOwnPropertyNames(a) // ???
上面代码中,数组的 length 属性是不可枚举的属性,所以只出现在 Object.getOwnPropertyNames 方法的返回结果中。 由于 JavaScript 没有提供计算对象属性个数的方法,所以可以用这两个方法代替。 var obj = { p1: 123, p2: 456 }; Object.keys(obj).length //2
object.getOwnPropertyNames(obj).length //??? 一般情况下,几乎总是使用 Object.keys 方法,遍历对象的属性。
Object.values() Object.values() 方法返回一个数组,成员是参数对象自身的(非继承的)所有可枚举属性的属性值。 var obj = { p1: 123, p2: 456 }; Object.values(obj) // [123, 456]
Object.entries() Object.entries() 方法返回一个数组,成员是参数对象自身的(非继承的)所有可枚举属性的键值对数组。 var obj = { p1: 123, p2: 456 }; Object.entries(obj) // [["p1", "123"], ["p2", 456]]
Object.prototype.hasOwnProperty() 实例对象的 hasOwnProperty() 方法接受一个字符串作为参数,返回一个布尔值,表示该实例对象自身是否具有该属性。有返回 true,没有或是继承的属性都返回 false。 var obj = { p: 123 };
obj.hasOwnProperty('p') // true obj.hasOwnProperty('toString') // false
思考: Object.keys("foo") Object.values("foo") Object.entries("foo")
原型链相关
Object.prototype 的原型是 null。 Object.getPrototypeOf(Object.prototype) === null // true
思考: Object.getPrototypeOf(Object)和Object.prototype得到的结果是相同的吗?
Object.prototype === Object.getPrototypeOf(new Object()); Object 本身是一個 函數,Object.prototype 不是 Object 這一對象的原型,而是 Obejct 這一函數的 函數原型,也就是 new Object() 的原型。
而 Object.getPrototypeOf(Object) 是把 Object 這一函數看作對象,返回的是 函數對象 的 原型,也就是 Function.prototype 即 function Empty() {} 了。
Object.setPrototypeOf() Object.setPrototypeOf() 方法为参数对象设置原型,返回该参数对象。它接受两个参数,第一个是现有对象,第二个是原型对象。 var a = {};var b = {x: 1};Object.setPrototypeOf(a, b); Object.getPrototypeOf(a) === b // true a.x // 1 new 命令可以使用 Object.setPrototypeOf() 方法模拟。 var F = function () { this.foo = 'bar'; }; var f = new F();// 等同于 var f = Object.setPrototypeOf({}, F.prototype); F.call(f);
Object.prototype.proto 实例对象的 proto 属性,返回该对象的原型。该属性可读写。 var obj = {};var p = {};
obj.proto = p;Object.getPrototypeOf(obj) === p // true 根据语言标准,proto 属性只有浏览器才需要部署,其他环境可以没有这个属性。它前后的两根下划线,表明它本质是一个内部属性,不应该对使用者暴露。因此,应该尽量少用这个属性,而是用 Object.getPrototypeof() 和 Object.setPrototypeOf(),进行原型对象的读写操作。
o2.isPrototypeOf(o3) // true o1.isPrototypeOf(o3) // true 只要实例对象处在参数对象的原型链上,isPrototypeOf() 方法都返回true。 Object.prototype.isPrototypeOf({}) // trueObject.prototype.isPrototypeOf([]) // trueObject.prototype.isPrototypeOf(/xyz/) // trueObject.prototype.isPrototypeOf(Object.create(null)) // false 由于 Object.prototype 处于原型链的最顶端,所以对各种实例都返回 true,只有直接继承自 null 的对象除外。
Object.getPrototypeOf(B) === A // true B.print() // hello B.print === A.print // true Object.create() 方法兼容性处理,即生成实例的本质: if (typeof Object.create !== 'function') { Object.create = function (obj) { function F() {} // 新建一个空的构造函数 F F.prototype = obj; // 让 F.prototype 属性指向参数对象 objreturn new F(); // 最后返回一个 F 的实例 }; } 下面三种方式生成的新对象是等价的: var obj1 = Object.create({}); var obj2 = Object.create(Object.prototype); var obj3 = new Object(); 如果想要生成一个不继承任何属性(比如没有 toString 和 valueOf 方法)的对象,可以将 Object.create 的参数设为 null。因为生成的实例对象原型是 null,所以它就不具备定义在 Object.prototype 原型上面的方法。 var obj = Object.create(null); Object.create() 方法还可以接受第二个参数。该参数是一个属性描述对象,它所描述的对象属性,会添加到实例对象,作为该对象自身的属性。 var obj = Object.create({}, { p1: { value: 123, enumerable: true, configurable: true, writable: true, }, p2: { value: 'abc', enumerable: true, configurable: true, writable: true, } }); // 等同于var obj = Object.create({}); obj.p1 = 123; obj.p2 = 'abc'; Object.create() 方法生成的对象,继承了它的原型对象的构造函数。 function A() {} var a = new A(); var b = Object.create(a);
b.constructor === A // true b instanceof A // true
思考: Object.create()和new Object的区别?
var person = { name:"a", sayName:function(){ console.log(this.name) } }
var myPerson1 = Object.create(person); myPerson1.sayName();
var myPerson2 = Object.create(person,{name:{value:'b'}}); myPerson2.name = 'c' console.log(myPerson2.name)
myPerson2.age = 'c' console.log(myPerson2)
用Object.create()创建新对象,没有想要改变原型的值,但是也不能改变创建的对象的属性值,但是用new Object可以。
new操作符——完成四件事 1.创建一个新的空对象; 2.绑定this到这个空对象,即切换上下文; 3.给这个新对象添加proto,并指向构造函数的原型; 4.如果构造函数未返回对象(如返回对象,则替换return this),则将该新对象返回,即return this(第2条:this指向新对象); 因此,new的伪代码类似如下: function new(Class){ const obj = {}; //step 1 Class.bind(obj); //step 2 obj.proto = Class.prototype; //step 3 return obj; //step 4 } 此外,第5件事: Class.prototype.constructor = Class; //循环引用 instance.constructor = Class; //可从上一句得到,instance先寻找own property,再寻找Class.prototype,找到constructor属性 例如 Dog.prototype.constructor = Dog; dog1.constructor = Dog;
Object.create vs new Object.create与new区别: 1.Object.create传入对象将作为新建对象的原型。 2.Object.create传入构造函数原型时,无论构造函数是否返回对象,Object.create只会return 以构造函数原型来新建的对象(参见下面的polyfill),而new则参见第4點 check this: function Dog(name){ const _name = name; function getName(){ return _name; } return { name : getName run : function(){} }; } Dog.prototype.setName = function(name){ this.name = name; }; const pet1 = new Dog('rocky'); console.log(pet1.name);
pet1.setName('skye');
pet1.run();
const pet2 = Object.create(Dog.prototype); pet2.setName('skye');
console.log(pet2.name);
pet2.run();
check polyfill from MDN: if (typeof Object.create !== "function") { Object.create = function (proto, propertiesObject) { if (typeof proto !== 'object' && typeof proto !== 'function') { throw new TypeError('Object prototype may only be an Object: ' + proto); } else if (proto === null) { throw new Error("This browser's implementation of Object.create is a shim and doesn't support 'null' as the first argument."); }
}
属性描述对象相关
obj.p1 // 123 obj.p2 // "abc" obj.p3 // "123abc" 注意,一旦定义了取值函数 get 或存值函数 set,就不能同时定义 writable 属性或 value 属性,否则会报错。 元属性默认值 Object.defineProperty() 和 Object.defineProperties() 参数里面的属性描述对象,writable、configurable、enumerable 这三个属性的默认值都为 false。 var obj = {};Object.defineProperty(obj, 'foo', {}); Object.getOwnPropertyDescriptor(obj, 'foo')// {// value: undefined,// writable: false,// enumerable: false,// configurable: false// }
obj.propertyIsEnumerable('p') // true obj.propertyIsEnumerable('toString') // false 注意,这个方法只能用于判断对象自身的属性,对于继承的属性一律返回 false。 控制对象状态相关 有时需要冻结对象的读写状态,防止对象被改变。JavaScript 提供了三种冻结方法,最弱的一种是 Object.preventExtensions(),其次是 Object.seal(),最强的是 Object.freeze()。
obj.p = 1; obj.p // undefined
Object.isExtensible() Object.isExtensible() 方法用于检查是否可以为一个对象添加属性。可以添加返回 true,不可以添加返回 false。 var obj = new Object(); Object.isExtensible(obj) // trueObject.preventExtensions(obj);Object.isExtensible(obj) // false
Object.seal() Object.seal() 方法使得一个对象既无法添加新属性,也无法删除旧属性。 var obj = { p: 'hello' }; Object.seal(obj); delete obj.p; obj.p // "hello"
obj.x = 'world'; obj.x // undefined Object.seal 实质是把属性描述对象的 configurable 属性设为 false,因此属性描述对象就不能再改变了。 var obj = { p: 'a' }; // seal方法之前Object.getOwnPropertyDescriptor(obj, 'p') // {... configurable: true }Object.seal(obj); // seal方法之后Object.getOwnPropertyDescriptor(obj, 'p') // {... configurable: false }Object.defineProperty(obj, 'p', { enumerable: false })// TypeError: Cannot redefine property: p Object.seal 只是禁止新增或删除属性,并不影响修改某个属性的值。 var obj = { p: 'a' };Object.seal(obj); obj.p = 'b'; obj.p // 'b' Object.seal 方法对 p 属性的 value 无效,是因为此时 p 属性的可写性由writable 决定。
obj.p = 'world'; obj.p // "hello"
obj.t = 'hello'; obj.t // undefineddelete obj.p // false obj.p // "hello"
思考: 1.怎么修改被冻结的对象 2.有没有完全冻结对象的方法
局限性 以上三个方法锁定对象有局限性,并不是完全冻结。
可以通过改变原型对象,来为对象增加新属性。 var obj = new Object(); Object.preventExtensions(obj); var proto = Object.getPrototypeOf(obj); proto.t = 'hello'; obj.t // hello 解决方案是,把 obj 的原型也冻结住。 Object.preventExtensions(proto);
proto.t = 'hello'; obj.t // undefined 如果属性值是对象,以上三个方法只能冻结属性指向的对象地址,而不能冻结对象本身。 var obj = { foo: 1, bar: ['a', 'b'] }; Object.freeze(obj);
obj.bar.push('c'); obj.bar // ["a", "b", "c"] obj.bar 属性指向一个数组,obj 对象被冻结以后,这个指向无法改变,即无法指向其他值,但是所指向的数组是可以改变的。 完全冻结 var constantize = (obj) => { Object.freeze(obj); Object.keys(obj).forEach((key, i) => { if ( typeof obj[key] === 'object' ) { constantize(obj[key]); } }); }; var obj = { foo: 1, bar: ['a', 'b'] }; constantize(obj);
obj.bar.push('c'); // TypeError: Cannot add property 2, object is not extensible 对象的拷贝
obj2.b.c = 3; obj1 // { a: 1, b: { c: 3 } }; obj2 // { a: 2, b: { c: 3 } }; 因此针对深拷贝,需要使用其他方法。 var obj1 = { a: 0 , b: { c: 0}};var obj2 = JSON.parse(JSON.stringify(obj1)); obj1.b.c = 4; obj2 // { a: 0, b: { c: 0}} Object.assign() 如果遇到存取器定义的属性,会只拷贝值。 var obj = { foo: 1, get bar() { return 2; } }; var copy = Object.assign({}, obj); copy // { foo: 1, bar: 2 } 因此必须使用 Object.getOwnPropertyDescriptors() 方法配合 Object.defineProperties() 方法,就可以实现正确拷贝。但仅限于可拷贝 getter 和 setter ,对于属性的引用类型还是属于浅拷贝。 var obj = { foo: { a : 0 }, get bar() { return 2; } };var target = Object.defineProperties({}, Object.getOwnPropertyDescriptors(obj) );Object.getOwnPropertyDescriptor(target, 'bar')// { get : ƒ bar(),set : undefined, enumerable : true, configurable : true }
obj.foo.a = 6 target.foo.a // 6 如果属性不可写,会引发报错,如果在引发错误之前添加了任何属性,则可以更改target对象。 其它
obj.toString = function () { return 'hello'; };
obj + ' ' + 'world' // "hello world" 数组、字符串、函数、Date 对象都分别部署了自定义的 toString 方法,覆盖了 Object.prototype.toString() 方法。 [1, 2, 3].toString() // "1,2,3"'123'.toString() // "123"
(function () { return 123; }).toString()// "function () {// return 123;// }"
(new Date()).toString()// "Tue May 10 2016 09:11:31 GMT+0800 (CST)" Object.prototype.toString.call(value) 可用于判断数据类型,详情见 判断数据类型的各种方法。