攻克object对象

Object

  • Object构造函数创建一个对象包装器。如果给定值是null或undefined,将返回一个空对象

改变原型链的方法

几乎所有的js对象都是Object的实例;一个典型的对象继承Object.prototype的属性及方法。改变Object原型会通过原型链改变所有对象。

  • Object.create(null)创建的对象
  • Object.setPrototypeOf()
  • obj.__proto__ = 原型对象

Object构造函数的属性

  • Object.length //值为1

  • Object.prototype :可以为所有Object类型的对象添加属性(现在一般用class类代替)

    • Object.prototype.constructor
      • 返回创建实例对象的Object构造函数的引用(new 关键字后面的值);注意,此属性的值是对函数本身的引用,而不是一个包含函数名称的字符串。对原始类型来说,如1true"test",该值只可读。
    1
    2
    3
    4
    var arr = [];
    arr.constructor //Array
    var obj = new zhuTou()
    obj.constrctor //zhuTou

Object构造函数的方法

Object.assign()复制

Object.assign( targetObj, obj1,obj2,…)

  • 将obj1,obj2等对象里的自身属性且为可枚举属性复制到targetObj对象上,属于浅拷贝。
  • 若有相同属性,则targetObj里的属性会被覆盖。
  • 返回值是一个与targetObj一样的对象,两者绝对相等。
  • Deep Clone let obj3 = JSON.parse(JSON.stringify(obj1)); obj1与obj3更改属性值互不影响。
  • 可拷贝symbol类型属性,继承属性和不可枚举属性不能被拷贝。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
let targetObj = {name:'targetObj'}
let obj1 = Object.create({foo:1},{
bar1:{
value:1 //bar是一个不可枚举属性
},
bar2:{
value:2,
enumerable:true //bar2是个自身可枚举属性
},
})
let obj2 = { [Symbol('foo')]:2,name:'obj1' };
let
let obj = Object.assign(targetObject,obj1,obj2)
{
name:'obj1', //后来居上,覆盖targetObj里的值
[Symbol('foo')]:2, //可拷贝symbol类型属性
bar2 : 2, //bar1不可枚举,bar2指定可枚举
}
  • 原始类型会被包装成对象
1
2
3
4
5
6
7
8
9
const v1 = "abc";
const v2 = true;
const v3 = 10;
const v4 = Symbol("foo")

const obj = Object.assign({}, v1, null, v2, undefined, v3, v4);
// 原始类型会被包装,null 和 undefined 会被忽略。
// 注意,只有字符串的包装对象才可能有自身可枚举属性。
console.log(obj); // { "0": "a", "1": "b", "2": "c" }
  • 异常会打断后续拷贝任务
1
2
3
4
5
6
7
8
9
10
11
12
13
14
const target = Object.defineProperty({}, "foo", {
value: 1,
writable: false
}); // target 的 foo 属性是个只读属性。

Object.assign(target, {bar: 2}, {foo2: 3, foo: 3, foo3: 3}, {baz: 4});
// TypeError: "foo" is read-only
// 注意这个异常是在拷贝第二个源对象的第二个属性时发生的。

console.log(target.bar); // 2,说明第一个源对象拷贝成功了。
console.log(target.foo2); // 3,说明第二个源对象的第一个属性也拷贝成功了。
console.log(target.foo); // 1,只读属性不能被覆盖,所以第二个源对象的第二个属性拷贝失败了。
console.log(target.foo3); // undefined,异常之后 assign 方法就退出了,第三个属性是不会被拷贝到的。
console.log(target.baz); // undefined,第三个源对象更是不会被拷贝到的。
  • 异常会打断后续拷贝任务。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
//以下函数会拷贝所有自有属性的属性描述符
function completeAssign(target,...sources){
sources.forEach(source => {
let descritors = Object.keys(source).reduce( (descriptors,key) => {
descriptors[key] = Object.getOwnPropertyDescriptor(source,key);
return descriptors;
},{});
//Object.assign默认也会拷贝可枚举的Symbols
object.getOwnPropertySymbols(source).forEach( sym => {
let descriptor = Object.getOwnPropertyDescriptor(source,sym);
if(descriptor.enumerable){
descriptors[sym] = descriptor;
}
});
Object.defineProperties(target,descriptors);
});
return target;
}
  • polyfill不支持 symbol 属性,因为ES5 中根本没有 symbol :
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
if (typeof Object.assign != 'function') {
// Must be writable: true, enumerable: false, configurable: true
Object.defineProperty(Object, "assign", {
value: function assign(target, varArgs) { // .length of function is 2
'use strict';
if (target == null) { // TypeError if undefined or null
throw new TypeError('Cannot convert undefined or null to object');
}

let to = Object(target);

for (var index = 1; index < arguments.length; index++) {
var nextSource = arguments[index];

if (nextSource != null) { // Skip over if undefined or null
for (let nextKey in nextSource) {
// Avoid bugs when hasOwnProperty is shadowed
if (Object.prototype.hasOwnProperty.call(nextSource, nextKey)) {
to[nextKey] = nextSource[nextKey];
}
}
}
}
return to;
},
writable: true,
configurable: true
});
}
Object.create()继承
  • Object(proto[,propertiesObject]):实现类式继承

    • proto:新创建的原型对象。
    • propertiesObject:未指定为undefined,要添加到新创建对象的可枚举属性(不是其原型链上的枚举属性,而是自定义属性)对象的属性描述符以及相应的属性名称。若为null或非原始包装对象抛出TypeError异常。
    • 返回一个带着指定原型对象和属性的新对象
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    function Shape(){	//父类
    this.x = 0;
    this.y = 0;
    }
    Shape.prototype.move = function(x,y){
    this.x += x;
    this.y += y;
    console.log(this,'shape moved');
    };
    function Rectangle(){
    Shape.call(this);
    }
    Rectangle.prototype = Object.create(Shape.prototype);
    Rectangle.prototype.constructor = Rectangle;//没有这句,输出的this指向父类Shape
    var rect = new Rectangle();
    console.log(rect instanceof Rectangle);
    console.log(rect instanceof Shape);
    rect.move();
    //true
    //true
    //Rectangle { x: NaN, y: NaN } 'shape moved'
    • 继承多个对象
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    function MyClass(){
    SuperClass.call(this);
    otherSuperClass.call(this);
    }
    //继承一个类
    MyClass.prototype = Object.create(SuperClass.prototype);
    //混合其他
    Object.assign(MyClass.prototype,OtherSuperclass.prototype);
    //重新指定constructor
    MyClass.prototype.constructor = MyClass;
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    var o;
    // 创建一个原型为null的空对象
    o = Object.create(null);

    o = {};

    // 以字面量方式创建的空对象就相当于:
    o = Object.create(Object.prototype);
    o = Object.create(Object.prototype, {
    // foo会成为所创建对象的数据属性
    foo: {
    writable:true,
    configurable:true,
    value: "hello"
    },
    // bar会成为所创建对象的访问器属性
    bar: {
    configurable: false,
    get: function() { return 10 },
    set: function(value) {
    console.log("Setting `o.bar` to", value);
    }
    }
    });


    function Constructor(){}
    o = new Constructor();
    // 上面的一句就相当于:
    o = Object.create(Constructor.prototype);
    // 当然,如果在Constructor函数中有一些初始化代码,Object.create不能执行那些代码


    // 创建一个以另一个空对象为原型,且拥有一个属性p的对象
    o = Object.create({}, { p: { value: 42 } })

    // 省略了的属性特性默认为false,所以属性p是不可写,不可枚举,不可配置的:
    o.p = 24
    o.p
    //42

    o.q = 12
    for (var prop in o) {
    console.log(prop)
    }
    //"q"

    delete o.p
    //false

    //创建一个可写的,可枚举的,可配置的属性p
    o2 = Object.create({}, {
    p: {
    value: 42,
    writable: true,
    enumerable: true,
    configurable: true
    }
    });
Object.defineProperties()
  • Object.defineProperties(obj,props):在对象obj上定义或修改现有属性,并返回该对象obj
  • props:要定义的其可枚举属性或修改的属性描述符对象。对象上存在的属性描述符主要有两种:数据描述符(有值的属性,该值可写,也可能不可写)和访问器描述符。描述符具有以下键:
    • configurable:默认为false。描述属性能否被修改或删除,一旦定义值后不可修改,否则会抛出错误。但是删除configurable属性值为false的属性值nothing happen,也不会报错。
    • enumerable:默认为false。能否使用for…in循环和Object.keys(),是否可枚举。
    • value:默认undefined。可为任何有效js值。
    • writable:默认false。是否可重写。
    • get:默认undefined。作为函数的getter函数,若无getter函数则为undefined。
    • set:与get同理。
object.defineProperty()
  • Object.defineProperty(obj,prop,descriptor):返回被传递给函数的对象,即obj,该方法式定义key为Symbol的属性的方法之一。
  • 通过简单赋值操作的普通属性可delete,通过该方法定义的属性值不能修改
  • 如果一个描述符不具有value,writable,get 和 set 任意一个关键字,那么它将被认为是一个数据描述符。如果一个描述符同时有(value或writable)和(get或set)关键字,将会产生一个异常。
  • 如果某些属性是继承来的,需要先冻结Object.prototype,明确指定所有属性,或通过Object.create(nill)将__proto__属性指向null。var des
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
//使用__proto__
var obj = {};
var descriptor = Object.create(null);//没有继承的属性
//默认没有enumerable,没有configurable,没有writable
descriptor.value="static";
Object.defineProperty(obj,"key",descriptor);

//显式定义
Object.defineProperty(obj,"key",{
enumerable:false,
confingurable:false,
writable:false,
value:'static'
});

//循环使用同一个对象
function withValue(value){
var d = withValue.d || (
withValue.d = {
enumerable:false,
configurable:false,
writable:false,
value:null
}
);
d.value = value;
return d;
}
Object.defineProperty(obj,"key",withValue("static"));

//如果freeze可用,防止代码添加或删除对象圆形的属性
(Object.freeze || object)(Object.prototype);
  • 如果对象中不存在指定的属性,Object.defineProperty()就创建这个属性。当描述符中省略某些字段时,这些字段将使用它们的默认值。拥有布尔值的字段的默认值都是falsevaluegetset字段的默认值为undefined。一个没有get/set/value/writable定义的属性被称为“通用的”,并被“键入”为一个数据描述符。
  • 如果访问者的属性是被继承的,它的get和set方法会在子对象的属性被修改或访问时调用。如果这些方法用一个变量存值,该值会被所有对象共享。
1
2
3
4
5
6
7
8
9
10
11
12
function myClass(){}
var value;
myClass.prototype.x = 2;
Object.defineProperty(myClass.prototype,"x",{
get(){ return value; },
set(x){ value = x; }
});
var a = new myClass();
var b = new myClass();
a.x = 1;
b.x //1
myClass.prototype.x //2 实例上的x与原型上的x不同,更改实例上的值不影响原型上的值
  • 这可通过将值存入另一个属性中解决,在get或set中,this指向某个被访问的被访问和修改属性的对象。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
function myClass(){}
Object.defineProperty(myClass.prototype,"x",{
get(){ return this.stored_x; },
set(x){ this._stored_x = x; }
});
myClass.prototype.x = 1;
Object.defineProperty(myClass.prototype,"y",{
writable:false,
value:"y"
});

var a = new myClass();
var b = new myClass();
a.x = 2;
b.x //undefined
Object.entries()
  • Object.entries(obj):返回一个给定对象自身可枚举属性的键值对数组,其排列顺序与for…in循环(会遍历原型链上的可枚举属性)返回顺序相同。如果是类数组则按数字键从小到大排列。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
const obj = { foo: 'bar', baz: 42 };
console.log(Object.entries(obj)); // [ ['foo', 'bar'], ['baz', 42] ]

// array like object
const obj = { 0: 'a', 1: 'b', 2: 'c' };
console.log(Object.entries(obj)); // [ ['0', 'a'], ['1', 'b'], ['2', 'c'] ]

// array like object with random key ordering
const anObj = { 100: 'a', 2: 'b', 7: 'c' };
console.log(Object.entries(anObj)); // [ ['2', 'b'], ['7', 'c'], ['100', 'a'] ]

// getFoo is property which isn't enumerable
const myObj = Object.create({}, { getFoo: { value() { return this.foo; } } });
myObj.foo = 'bar';
console.log(Object.entries(myObj)); // [ ['foo', 'bar'] ]

// non-object argument will be coerced to an object
console.log(Object.entries('foo')); // [ ['0', 'f'], ['1', 'o'], ['2', 'o'] ]

// iterate through key-value gracefully
const obj = { a: 5, b: 7, c: 9 };
for (const [key, value] of Object.entries(obj)) {
console.log(`${key} ${value}`); // "a 5", "b 7", "c 9"
}

// Or, using array extras
Object.entries(obj).forEach(([key, value]) => {
console.log(`${key} ${value}`); // "a 5", "b 7", "c 9"
});
Object.freeze()
  • Object.freeze(obj):将obj对象冻结,不能对其自身属性进行任何操作,同时该对象的原型也不能修改。如果一个属性的值是个对象,则这个对象中的属性是可以修改的,除非它也是个冻结对象。数组作为一种对象,被冻结,其元素不能被修改。没有数组元素可以被添加或移除。
  • 这个方法返回传递的对象obj,而不是创建一个被冻结的副本。
1
2
3
4
5
6
//浅冻结
obj1 = { name:'nikita', internal:{} };
Object.freeze(obj1);
obj1.name = 'hello'
obj1.internal.a = 1;
obj1 //{name: "nikita", internal: {a:1}}
  • 递归冻结每个对象属性,即深度冻结
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
function deepFreeze(obj){
//取回定义在obj上的属性名
var propNames = object.getPropertyName(obj);
//在冻结对象之前冻结属性
propNames.forEach(function(name){
var prop = obj[name];
if(typeof prop == 'object' && prop !== null){
deepFreeze(prop);
}
});
//冻结自身
return Object.freeze(obj);
}
obj2 = {
internal: {}
};

deepFreeze(obj2);
obj2.internal.a = 'anotherValue';
obj2.internal.a; // undefined
Object.getOwnPropertyDescriptor()
  • Object.getOwnPropertyDescriptor(obj,prop):返回指定对象自身属性的属性描述符。
1
2
3
4
5
6
7
8
9
10
var o, d;

o = { get foo() { return 17; } };
d = Object.getOwnPropertyDescriptor(o, "foo");
// d {
// configurable: true,
// enumerable: true,
// get: /*the getter function*/,
// set: undefined
// }
  • 若第一个参数不是对象,ES5报错,ES6强制转换为对象。
1
2
3
4
5
6
7
8
9
10
Object.getOwnPropertyDescriptor('foo', 0);
// 类型错误: "foo" 不是一个对象 // ES5 code

Object.getOwnPropertyDescriptor('foo', 0);
// Object returned by ES2015 code: {
// configurable: false,
// enumerable: true,
// value: "f",
// writable: false
// }
  • Object.getOwnPropertyDescriptors() 方法用来获取一个对象的所有自身属性的描述符。
  • 浅拷贝一个对象
1
2
3
4
Object.create(
Object.getPrototypeOf(obj),
Object.getownpropertyDescriptors(obj)
);
  • 创建子类
1
2
3
4
function superClass(){}
superClass.prototype={}
function subClass = {}
subClass.prototype = Object.create(superClass.prototype,Object.getOwnpropertyDescriptors({}));
Object.getOwnPropertyNames(obj)
  • 返回一个包含所有属性名(包括不可枚举属性但不包括Symbol值作为名称的属性)组成的数组
Object.keys(obj)
  • 返回自身属性的字符串数组
Object.values(obj)
  • 返回给定对象自身的所有可枚举属性值的数组。
Object.is()

object.is(val1,val2):判断两个值是否是相同的值,作用与”==”相似,只不过Object.is()不会隐式类型转换,返回boolean值。

注意相等的几种情况

  • 两个值都是 undefined
  • 两个值都是 null
  • 两个值都是 true 或者都是 false
  • 两个值是由相同个数的字符按照相同的顺序组成的字符串
  • 两个值指向同一个对象
  • 两个值都是数字并且
    • 都是正零 +0
    • 都是负零 -0
    • 都是 NaN
    • 都是除零和 NaN 外的其它同一个数字
1
2
3
4
5
// 特例
Object.is(0, -0); // false
Object.is(0, +0); // true
Object.is(-0, -0); // true
Object.is(NaN, 0/0); // true
Object.isExtensible(obj)
  • 检测对象obj是否可扩展(能否加新的属性),返回boolean值。
  • 默认是可扩展的,即可为他们添加新的属性。以及他们的__prop__属性也是可被更改的。Object.preventExtensions,Object.seal或Object.freeze方法都可使对象obj变成不可扩展。
  • 若参数不是object类型,ES5会报错,ES6会将其转为不可扩展的普通对象.
Object.isFrozen(obj)
  • 判断对象obj是否被冻结,冻结指它不可扩展,所有属性所有属性都是不可配置的,且所有数据属性(即没有getter或setter组件的访问器)都是不可写的。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
var oneProp = {p:42}
// 让这个对象变的不可扩展,并不意味着这个对象变成了冻结对象,
// 因为p属性仍然是可以配置的(而且可写的).
Object.preventExtensions(oneProp);
Object.isFrozen(oneProp) //=== false
//此时如果删除了这个属性,则它会变成一个冻结对象
delete oneProp.p;
Object.isFrozen(oneProp) //===true
//一个不可扩展的对象,拥有一个不可写但可配置的属性,则它仍是不可冻结的
var nonWritable = {e:'plep'};
Object.preventExtensions(nonWritable);
Object.defineProperty(noWritable,'e',{writable:false});
Object.isFrozen(nonWritable) //===false
//将该属性变为不可配置则可让其变为冻结对象
Object.defineProperty(noWritable,'e',{configurable:false});
Object.isFrozen(nonWritable) //===true
  • 如果参数不是对象类型,ES5报错。ES6将其视为一个冻结的普通对象。
Object.isSealed(obj)
  • 判断一个对象是否被密封,返回boolean值。密封对象是指那些不可扩展且所有自身属性都不可配置且不可删除(但不一定是不可写的)的对象。
Object.hasOwnProperty(str/symbol)
  • 判断某个属性是否为对象自身属性,与in不同,该方法会忽略掉从原型链上继承来的属性。即使属性值为null或undefined,只要属性存在都返回true
Object.toString()
  • 返回【Object type】
1
2
3
4
5
6
7
8
9
var toString = Object.prototype.toString;

toString.call(new Date); // [object Date]
toString.call(new String); // [object String]
toString.call(Math); // [object Math]

//Since JavaScript 1.8.5
toString.call(undefined); // [object Undefined]
toString.call(null); // [object Null]
obj.valueOf()
  • 返回指定对象的原始值
对象 返回值
Array 返回数组对象本身。
Boolean 布尔值。
Date 存储的时间是从 1970 年 1 月 1 日午夜开始计的毫秒数 UTC。
Function 函数本身。
Number 数字值。
Object 对象本身。这是默认情况。
String 字符串值。
Math 和 Error 对象没有 valueOf 方法。

注意:字符串上下文中的对象通过 toString()方法转换,这与使用valueOf转换为原始字符串的String对象不同。所有对象都能转换成一个“[object *类型*]”这种格式的字符串。但是很多对象不能转换为数字,布尔或函数。

1
2
3
4
5
6
// new一个Boolean对象
var newBool = new Boolean(true);
// valueOf()返回的是true,两者的值相等
console.log(newBool.valueOf() == newBool); // true
// 但是不全等,两者类型不相等,前者是boolean类型,后者是object类型
console.log(newBool.valueOf() === newBool); // false
Object.seal(prop)

**Object.seal()**方法封闭一个对象,阻止添加新属性并将所有现有属性标记为不可配置。当前属性的值只要可写就可以改变。

文章目录
  1. 1. Object
    1. 1.0.1. 改变原型链的方法
    2. 1.0.2. Object构造函数的属性
    3. 1.0.3. Object构造函数的方法
      1. 1.0.3.0.1. Object.assign()复制
      2. 1.0.3.0.2. Object.create()继承
      3. 1.0.3.0.3. Object.defineProperties()
      4. 1.0.3.0.4. object.defineProperty()
      5. 1.0.3.0.5. Object.entries()
      6. 1.0.3.0.6. Object.freeze()
      7. 1.0.3.0.7. Object.getOwnPropertyDescriptor()
      8. 1.0.3.0.8. Object.getOwnPropertyNames(obj)
      9. 1.0.3.0.9. Object.keys(obj)
      10. 1.0.3.0.10. Object.values(obj)
      11. 1.0.3.0.11. Object.is()
      12. 1.0.3.0.12. Object.isExtensible(obj)
      13. 1.0.3.0.13. Object.isFrozen(obj)
      14. 1.0.3.0.14. Object.isSealed(obj)
      15. 1.0.3.0.15. Object.hasOwnProperty(str/symbol)
      16. 1.0.3.0.16. Object.toString()
      17. 1.0.3.0.17. obj.valueOf()
      18. 1.0.3.0.18. Object.seal(prop)
|