对象和Object构造函数

对象和Object构造函数

没有单独区分出es6新增的一些属性方法

1.对象

创建一个对象

1
2
3
4
5
6
7
8
9
10
var obj = {};
var obj1 = Object();//参数接受任何值,如果是null或者undefined,则返回一个空对象,否则返回给定的值的对应类型的对象
var obj2 = new Object();//同上
var obj3 = Object.create({});//这里参数接受一个对象或者null,下面有详细的说明

//它们之间的区别
console.log(obj.__proto__ === Object.prototype);//true
console.log(obj1.__proto__ === Object.prototype);//true
console.log(obj2.__proto__ === Object.prototype);//true
console.log(obj3.__proto__.__proto__ === Object.prototype);//true

还可以通过new关键字创建一个对象实例

对象的属性

1) 如何设置以及访问对象的属性
1
2
3
4
5
6
7
8
9
10
11
12
var obj = {};
var objKey = {};
var key = "propertyName";
obj.a = 1;
obj[key] = 2;//可以通过存储在变量中的字符串设置属性
obj[objKey] = 'Object';

console.log(obj.a);//1
//通过方括号访问
console.log(obj['a']);//1
console.log(obj.propertyName);//2
console.log(obj['[object Object]']);//Object

这里有两个问题:

第一个问题:console.log(obj['[object Object]']),为什么设置属性的时候,键名是objKey这个对象,最后打印的时候键名要使用[object Object]字符串

因为:在objKey被添加到obj的时候调用了objKey.toString()方法,将返回的字符串作为了新键。

第二个问题:objKey对象上并没有toString方法,所以就要爬原型链了,从__proto__上找,其实也就是Object.prototype.toString方法。通常我们用这个方法来区分对象和数组。

2)怎样的属性只支持使用方括号访问对象属性
1
2
3
4
5
6
7
8
9
var obj = {
1:'number',
'-':'-',
" space":'space'
}

console.log(obj[1]);//number
console.log(obj["-"]);//-
console.log(obj[" space"]);//space

当对象的属性是空格,分隔符,或者数字开头的时候,只能通过方括号取得它的值,因为它们不是有效的JavaScript标识符

3)枚举对象中所有属性

for…in…

1
2
3
4
5
6
7
var obj = {a:1,b:2,c:3};
for(var i in obj){
console.log(i);
}
//a
//b
//c

Object.keys(obj)

返回一个数组

1
2
3
var obj = {a:1,b:2,c:3};
var arr = Object.keys(obj);
console.log(arr);//[ 'a', 'b', 'c' ]

Object.getOwnPropertyNames(obj)

英文直译:获取私有的属性名字们,哈哈哈哈,返回一个数组

1
2
3
var obj = {a:1,b:2,c:3};
var arr = Object.getOwnPropertyNames(obj);
console.log(arr);//[ 'a', 'b', 'c' ]
4)getter与setter

getter:获取某个特定属性的值的方法

setter:设定某个特定属性的值的方法

1
2
3
4
5
6
7
8
9
10
11
12
13
var obj = {
a: 1,
set c(x) {
this.a = x;
},
get c() {
return this.a + 1;
}
}
console.log(obj.a);//1
console.log(obj.c);//2
obj.c = 3;
console.log(obj.a);//3

2.Object构造函数

构造函数的方法

1)Object.assign(target, …sources)

对象合并,并且创建一个新的对象

target:目标对象
sources:原对象

1
2
3
4
var obj1 = { a: 2, c: 3 };
var obj2 = { a: 1, b: 2 };
var obj3 = Object.assign(obj1, obj2);
console.log(obj3);//{ a: 1, c: 3, b: 2 }
2)Object.create(proto, [propertiesObject])

创建一个新的对象

proto:新创建对象的原型对象

propertiesObject:为新创建的对象添加可枚举的属性,如果不是null或者对象,则抛出异常

1
2
3
4
5
6
7
8
var obj = {a:1,b:2};
var obj1 = Object.create(obj,{
c:{
writable:true,
value:3
}
});
console.log(obj1);

打印出的obj1如图:

WX20190216-143623@2x

可以看到这个新对象,而它的__proto__指向obj,也就是说obj1.__proto__.__proto__ === Object.prototype

也就是说这个方法可以设置新建对象的__proto__指向,如果第一个参数是null的话

1
2
var obj = Object.create(null);
console.log(obj);

输出如图:

WX20190216-144352@2x

实现一个简单create方法

1
2
3
4
5
Object.create =  function (o) {
var F = function () {};
F.prototype = o;
return new F();
};
3)Object.defineProperty(obj, prop, descriptor)

在一个对象上定义新的属性,或者修改已有的属性

obj:要在其上定义属性的对象。

prop:要定义或修改的属性的名称。

descriptor:将被定义的属性描述符

属性描述符

configurable(默认值false):是否能删除对象上的属性。

enumerable(默认值false):是否能够枚举,比如for…in…

writable(默认值false):是否能修改属性值

value(默认值undefined):属性的值

get(默认值undefined):访问该属性的时候会执行对应的方法,没有参数传入

set(默认值undefined):访问该属性的时候会执行对应的方法,有参数传入

需要注意的就是:
1.如果第一次没设置属性描述符,则会使用默认值,修改(设置为相同的值则不算修改)属性值的时候(除了value和writable)会扔出TypeError,报错信息大概是这样:

WX20190216-235731@2x

2.设置了set或get,同时设置了value或者writable就报错了,报错信息如图:

WX20190217-233153@2x

这里是🌰,看一看属性描述符的特点:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
var obj = {};
Object.defineProperty(obj, 'name', {
value: 'qinhanwen',
});

for(var i in obj){
console.log(i);//没有打印
}

console.log(obj.name);//qinhanwen
delete obj.name;
console.log(obj.name);//qinhanwen 可以看到没删除成功


obj.name = 'zenghua';
console.log(obj.name);//qinhanwen 可以看到没修改成功

这里configurable,enumerable,writable默认值false。我们重新写一个设置这些属性为true

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
var obj = {};
Object.defineProperty(obj, 'name', {
value: 'qinhanwen',
configurable: true,
enumerable: true,
writable: true,
});

for (var i in obj) {
console.log(i);//name
}

console.log(obj.name);//qinhanwen
delete obj.name;
console.log(obj.name);//undefined


obj.name = 'zenghua';
console.log(obj.name);//zenghua
那么来实现一个简单的双向绑定吧!!!
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
<!DOCTYPE html>
<html lang="en">

<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Document</title>
</head>

<body>
<input type="text" id="input" />
<p id="p"></p>
</body>
<script>
var P_TAG = document.getElementById('p');
var INPUT_TAG = document.getElementById('input');

var obj = {};
INPUT_TAG.onkeyup = function(){
obj.name = INPUT_TAG.value;
}
Object.defineProperty(obj, 'name', {
set: function (x) {
P_TAG.innerHTML = x;
INPUT_TAG.value = x;
},
});

</script>
1
</html>

如图:

WX20190218-092532@2x

通过JS修改obj.name的值的时候,inputvaluepinnerHTML都会改变。

4)Object.defineProperties(obj, props)

为对象设置多个属性

obj:要在其上定义属性的对象。

props:定义其可枚举属性或修改的属性描述符的对象

1
2
3
4
5
6
7
8
9
10
11
12
13
var obj = {};
Object.defineProperties(obj, {
name: {
value: 'qinhanwen',
writable: true,
enumerable: true
},
age: {
value: '25',
enumerable: true
}
})
console.log(obj);//{ name: 'qinhanwen', age: '25' }

Object.definePropertyObject.defineProperties的区别:

它们接受的参数不同,Object.defineProperty为单个键名设置属性描述符,而Object.defineProperties第二个参数接受个对象是可以一次性设置多个键名与对应的属性操作符

5)Object.freeze(obj)

冻结对一个对象,也就是说对冻结的对象的任何属性都不能修改,这个方法返回被冻结的对象,而不是创建一个被冻结的副本

1
2
3
4
var obj = {a:1};
Object.freeze(obj);
obj.a = 2;
console.log(obj.a);//1
6)Object.getOwnPropertyDescriptor(obj, prop)

返回指定对象上的键名对应的属性描述符

obj:指定的对象

prop:指定的键名

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
var obj = {};
Object.defineProperty(obj, 'name', {
value: 'qinhanwen',
writable: true,
enumerable: true,
configurable: true
});
console.log(obj);//{ name: 'qinhanwen' }
console.log(Object.getOwnPropertyDescriptor(obj, 'name'));
/*
*{ value: 'qinhanwen',
* writable: true,
* enumerable: true,
* configurable: true
*}
*/
7)Object.getOwnPropertyDescriptors(obj)

上面那个方法需要指定键名,这个方法只需要指定对象

obj:指定的对象

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
var obj = {};
Object.defineProperties(obj, {
name:{
value: 'qinhanwen',
writable: true,
enumerable: true,
configurable: true
},
age:{
value: 25,
writable: true,
enumerable: true,
configurable: true
}
});
console.log(obj);
console.log(Object.getOwnPropertyDescriptors(obj, 'name'));

打印结果如图:

WX20190218-130550@2x

8)Object.getOwnPropertyNames(obj)

返回对象的键名的数组(包括不可枚举的键名)

obj:指定的对象

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
var obj = {};
Object.defineProperties(obj, {
name:{
value: 'qinhanwen',
writable: true,
enumerable: true,
configurable: true
},
age:{
value: 25,
writable: true,
enumerable: false,
configurable: true
}
});
console.log(Object.getOwnPropertyNames(obj));//[ 'name', 'age' ]
9)Object.getPrototypeOf(obj)

返回obj的[[Prototype]]属性值

obj:指定的对象

1
2
3
4
var obj = new Object({});
var obj1 = Object.create(obj);
console.log(Object.getPrototypeOf(obj) === Object.prototype);//true
console.log(Object.getPrototypeOf(obj1) === obj);//true
10)Object.setPrototypeOf(obj,prototype)

设置一个指定的对象的原型 就是设置对象 [[Prototype]] 属性指向到另一个对象null

obj:指定的对象

prototype:该对象的新原型(一个对象或者null)否则报错TypeError

1
2
3
4
var obj = { name: 'qinhanwen' };
var obj1 = { age: 25 };
Object.setPrototypeOf(obj, obj1);
console.log(obj);

打印的obj如图:

WX20190218-132239@2x

可以看到obj.__proto__ === obj1obj.__proto__.__proto__ === Object.prototype

11)Object.is(value1, value2)

判断两个值是否相同

value1:第一个值

value2:第二个值

1
2
3
4
5
console.log(Object.is("", false));//false
console.log("" == false);//true

console.log(Object.is(+0, -0));//false
console.log(+0 === -0);//true

我们都知道==如果两边的值的类型不同的话,则会把两边的值先进行隐式转换,之后再进行比较。

而它又与===不同。

12)Object.keys(obj)

获得可枚举的对象的键名,返回个数组。

obj:指定的对象

1
2
var obj = {name:'qinhanwen',age:25};
console.log(Object.keys(obj));//[ 'name', 'age' ]
13)Object.values(obj)

返回可被枚举的键值

obj:指定的对象

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
var obj = {};
Object.defineProperties(obj, {
name:{
value: 'qinhanwen',
writable: true,
enumerable: true,
configurable: true
},
age:{
value: 25,
writable: true,
enumerable: false,
configurable: true
}
});
console.log(Object.values(obj));//["qinhanwen"]

Object构造函数的prototype上的方法

1)object.prototype.hasOwnProperty()

返回布尔值,判断对象上是否有某个键名

语法:obj.hasOwnProperty(prop)

prop:键名

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
var obj = {};
Object.defineProperties(obj, {
name:{
value: 'qinhanwen',
writable: true,
enumerable: true,
configurable: true
},
age:{
value: 25,
writable: true,
enumerable: false,
configurable: true
}
});
console.log(obj.hasOwnProperty('name'));//true
console.log(obj.hasOwnProperty('age'));//true

说到这个方法,不得不说一下in操作符

1
2
3
4
5
6
7
8
9
10
var fn = function(){
}
fn.prototype.name = 'qinhanwen';

var fn1 = new fn();
console.log(fn1.hasOwnProperty('name'));//false
console.log('name' in fn1);//true
fn1.name = 'qinhanwen';
console.log(fn1.hasOwnProperty('name'));//true
console.log('name' in fn1);//true

可以看出hasOwnProperty方法是判断实例上是否有某个属性,而in操作符会判断实例,没有的话会往[[Prototype]]上找。

2)Object.prototype.isPrototypeOf()

返回布尔值,判断一个对象是否在另一个对象的原型链上

语法:prototypeObj.isPrototypeOf(obj)

obj:目标对象

prototypeObj:原型对象

1
2
3
4
5
var obj = {};
var obj1 = Object.create(obj);
console.log(obj1.__proto__.__proto__ == Object.prototype);//true
console.log(obj.isPrototypeOf(obj1));//true
console.log(Object.isPrototypeOf(obj1));//true

说到这个方法,让我想到了instanceOf,判断某个值是否在另外一个值的原型链上,实现一个instanceOf方法

3)Object.prototype.toString()

返回一个表示该对象的字符串

语法:object.toString()

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
var obj = new Object();
console.log(obj.toString());//[object Object]

//其他类型的数据toString方法,打印的结果却不同
var obj = new Object();
console.log(obj.toString());//[object Object]

var arr = [];
console.log(arr.toString());//""

var boolean = true;
console.log(boolean.toString());//"true"

var num = 1;
console.log(num.toString());"1"

其他的类型作为Object的实例,都重写了toString方法:

1
2
3
var arr = [];
delete Array.prototype.toString;
console.log(arr.toString());//[object Array]

这里我们删除了arr的__proto__上的toString方法,就会顺着原型链往上爬,寻找toString方法