Object对象中的相关方法
Object对象的原生方法分成两类:Object本身的方法与Object的实例方法。
所谓本身的方法就是直接定义在当前构造函数对象Object对象上的也称之为静态方法
//例子
Object.xxx()
Object实例方法就是定义在Object.prototype的方法。它可以被Object实例直接共享
Object.prototype.hello = function(){
console.log('hello');
}
var obj = new Object();
obj.hello();//hello
Object的静态方法
所谓静态方法,是指定制在Object对象本身的方法
Object.keys()
获取属性名的,keys()用来遍历对象的属性。能传一个参数,参数是一个对象,返回是一个数组,该数组都是对象自身的所有属性名。
var arr=["1","2","3","4"]
console.log(Object.keys(arr)) //获取出数组的键
var obj={
1:"w",
4:"c",
3:"d"
}
console.log(Object.keys(obj)) //获取出数组并从小到大排列
Obj.getOwnPropertyNames()
和上面keys方法是类似的,也是接收一个参数,也返回一个数组,包含了该对象的所有属性。
Object.getOwnPropertyNames
方法与Object.keys
类似,也是接受一个对象作为参数,返回一个数组,包含了该对象自身的所有属性名。
var obj={
1:"w",
4:"c",
5:"d"
}
console.log(Object.getOwnPropertyNames(obj))
对于一般的对象来说,
Object.keys()
和Object.getOwnPropertyNames()
返回的结果是一样的。只有涉及不可枚举属性时,才会有不一样的结果。Object.keys
方法只返回可枚举的属性,Object.getOwnPropertyNames
方法还返回不可枚举的属性名。
枚举:可以理解为一个属性能不能被遍历,能被遍历就称为可枚举的属性,不能被遍历就称为不能被枚举的属性
var arr=["a","b","c","d"]
console.log(Object.getOwnPropertyNames(arr))
上面代码中,数组的
length
属性是不可枚举的属性,所以只出现在Object.getOwnPropertyNames
方法的返回结果中。
由于 JavaScript 没有提供计算对象属性个数的方法,所以可以用这两个方法代替。
var obj = {
0:'a',
1:'b',
2: 'c'
}
Object.keys(obj).length //3
Object.getOwnPropertyNames(obj).length //3
一般情况下,使用Object.keys方法遍历对象的属性应用最多。
Object.getPrototypeOf()
Object.getPrototypeOf
方法,参数是该对象,返回参数是该对象的原型。这是获取原型对象的标准方法
function fn(){};
var f1=new fn();
console.log(Object.getPrototypeOf(f1))
console.log(Object.getPrototypeOf(f1)=== fn.prototype)
通过该方法知道实例f1这个对象的原型是fn的prototype。
在这个时候我们有一些特殊的原型:
// 空对象的原型是 Object.prototype
Object.getPrototypeOf({}) === Object.prototype // true
// Object.prototype 的原型是 null
Object.getPrototypeOf(Object.prototype) === null // true
// 函数的原型是 Function.prototype
function f() {}
Object.getPrototypeOf(f) === Function.prototype // true
Object.setPrototypeOf()
这个方法接收两个参数,第一个参数是现有对象,第二个是原型对象。
var a = {};
var b = {x : 1};
Object.setPrototypeOf(a,b);
console.log(Object.getPrototypeOf(a));//{x:1}
new命令可以使用Object.setPrototypeOf方法模拟。
var F = function () {
this.foo = 'bar';
};
var f = new F();
console.log(f)
// 等同于
var f = Object.setPrototypeOf({}, F.prototype);
F.call(f);
console.log(f)
Object.create()
生成实例对象的常用方法是,使用new
命令让构造函数返回一个实例。但是很多时候,只能拿到一个实例对象,它可能根本不是由构建函数生成的,那么能不能从一个实例对象,生成另一个实例对象呢?
JavaScript
提供了Object.create
方法,用来满足这种需求。该方法接收一个对象作为参数,然后以它为原型,返回一个实例对象。该实例完全继承原型对象的属性。
// 原型对象
var A = {
hello: function () {
console.log('hello');
}
};
// 实例对象
var B = Object.create(A);
console.log(Object.getPrototypeOf(B) === A )// true
B.hello() // hello
console.log(B.hello === A.hello )// true
B
对象通过Object.create
来生成并且B
还继承A
(var B = Object.create(A);
)把A
对象作为B
的原型返回一个新的实例,通过 Object.getPrototypeOf(B)
获取B
的原型是否为A
。B
和A
都有hello
这个方法,B
可以去调用hello
这个方法。
以A对象为原型生成B对象,那么B就能继承A。 除了上面提到的两个方法,Object还有不少的其它静态方法,后文咱们会一一详细讲解
其它方法
1. 对象属性模型的相关方法
Object.getOwnPropertyDescriptor():获取某个属性的描述对象。 Object.defineProperty():通过描述对象,定义某个属性。 Object.defineProperties():通过描述对象,定义多个属性。
2. 控制对象状态的方法(了解部分)
Object.preventExtensions():防止对象扩展。 Object.isExtensible():判断对象是否可扩展。 Object.seal():禁止对象配置。 Object.isSealed():判断一个对象是否可配置。 Object.freeze():冻结一个对象。 Object.isFrozen():判断一个对象是否被冻结。
Object的实例方法
方法定义在Object.prototype对象上。我们称之为实例方法,所有Object的实例对象都继承了这些方法
Object实例对象的方法,主要有以下六个。
- Object.prototype.valueOf():返回当前对象对应的值。
- Object.prototype.toString():返回当前对象对应的字符串形式。
- Object.prototype.toLocaleString():返回当前对象对应的本地字符串形式。
- Object.prototype.hasOwnProperty():判断某个属性是否为当前对象自身的属性,还是继承自原型对象的属性。
- Object.prototype.isPrototypeOf():判断当前对象是否为另一个对象的原型。
- Object.prototype.propertyIsEnumerable():判断某个属性是否可枚举。
Object.prototype.valueOf():
返回当前对象对应的值。默认情况下是返回对象本身。
var obj = new Object();
obj.valueOf() === obj;//true
用途
可以在自动类型转换的时候默认使用这个方法
var obj = new Object();
//JavaScript就会默认调用valueOf()方法,求出obj的值再与1相加
console.log(1+obj);//"1[object Object]"
所以,如果自定义valueOf方法,就可以得到想要的结果
比如说现在调用一下1+obj
想要结果等于3
。使用obj
调用valueOf
(obj.valueOf
),重写一下这个函数,返回出一个2
,obj+1
执行的时候就会隐式的去调用一下valueOf
,最终得到想要的结果
var obj = new Object();
obj.valueOf = function(){ //这里重写函数
return 2;
}
console.log(1 + obj);//3 obj隐式调用valueOf,此时valueOf为2
用定义的Object.valueOf
覆盖Object.prototype.valueOf
实现原理: 给当前实例对象定制一个valueOf属性,给一个函数,找obj的时候先去找自己的valueOf
,发现这里要返回2,那就直接返回2,就不再根据原型链向上查找了。
Object.prototype.toString()
返回当前对象对应的字符串形式。默认是返回字符串的类型。
var obj1 = new Object();
console.log(obj1.toString());//"[object Object]"
var obj2 = {a:1};
obj2.toString() // "[object Object]"
返回的这个结果说明了对象的类型。
字符串[object Object]
本身没有太大的用处,但是通过自定义toString
方法,可以让对象在自动类型转换时,得到想要的字符串形式。
var obj = new Object();
obj.toString = function(){
return 'hello';
}
console.log(obj + '' + 'world');//"hello world"
像数组、字符串、函数、
Date
对象都分别定义了自定义的toString
方法,覆盖了Object.prototype.toString()
方法
console.log([1, 2, 3].toString() );// "1,2,3"
console.log('123'.toString()); // "123"
console.log((function(){
return 123
}).toString());
console.log((new Date()).toString())
Object.prototype.toLocaleString()
返回当前对象对应的本地字符串形式。 方法跟toString方法用法一致。
目前,主要有三个对象自定义了toLocaleString
方法
Array.prototype.toLocaleString()
Number.prototype.toLocaleString()
Date.prototype.toLocaleString()
举例来说,日期的实例对象的toString
和toLocaleString
返回值就不一样,而且toLocaleString
的返回值跟用户设定的所在地域相关
var date = new Date();
console.log(date.toString()) // "Tue Jan 01 2018 12:01:33 GMT+0800 (CST)"
console.log(date.toLocaleString()) // "1/01/2018, 12:01:33 PM"
toString
是国际性的、统一的。toLocaleString
是根据所在地返回我们对应的结果
Object.prototype.isPrototypeOf()
用来判断该对象是否是另一个对象的原型
var o1 = {};
var o2 = Object.create(o1);
var o3 = Object.create(o2);
console.log(o2.isPrototypeOf(o3));//true
console.log(o1.isPrototypeOf(o3));//true
上面代码中,
o1
和o2
都是o3
的原型。这表明只要实例对象处在参数对象的原型链上,isPrototypeOf
方法都返回true
。
//Object.prototype是否为对象的原型
console.log(Object.prototype.isPrototypeOf({})); // true
//Object.prototype是否为数组的原型
console.log(Object.prototype.isPrototypeOf([])); // true
//Object.prototype是否为null的原型
console.log(Object.prototype.isPrototypeOf(Object.create(null))); // false
把
null
作为一个原型,object
的上一层就是null
也就是最顶层。那它当然不是Object.prototype
。所以返回false
。通过这个例子也能看出来,Object.prototype
是原型链的最顶端,所以各种实例,像数组、对象都会返回true
。只有直接继承null
这个对象除外,因为它不是object.prototype
的原型
每一个实例对象里面都有一个
__proto__
,同样的情况下吧object.prototype
作为一个实例对象,这里面也有一个__proto__
,它也指向它的原型对象,
console.log(Object.prototype.__proto__); // null
Object.prototype
作为一个实例原型指向null
Object.prototype.hasOwnProperty()
判断一下当前这个实例对象是否有他自己的属性,如果有这个属性返回true
,如果没有则返回false
。
var obj = {
a: 123
}
console.log(obj.hasOwnProperty('b'));//false
console.log(obj.hasOwnProperty('a'));//true
我们知道
obj
实例对象继承了Object
原型上的属性,比如通过Object
调用toString()
,toString()
作为一个被继承来的属性,返回false
。
var obj = {
a: 123
}
console.log(obj.hasOwnProperty('toString'));//false 判断是否有toString属性
Object.prototype.hasOwnProperty
方法接受一个字符串作为参数,返回一个布尔值,表示该实例对象自身是否具有该属性。只校验实例的自身的属性,继承来的属性返回为false
。
属性描述对象
JavaScript提供了一个内部数据结构,用来描述对象的属性,控制它的行为,比如该属性是否可写、可遍历等等。这个内部数据结构称之为”属性描述对象”。每个属性都有自己对应的属性描述对象,保存该属性的一些元信息
var obj={};
obj.name="小太阳";
obj.name="小胖子" ; //name是可被修改的
console.log(obj.propertyIsEnumerable("name")) ; //可枚举可以被遍历
console.log(obj.name); //能打印 小胖子
console.log(delete obj.name); //可被删除的
{
value: 小胖子,
writable: false, //是否有可写性,是否能被修改
enumerable: true, //可枚举吗?内否遍历
configurable: false, //能否被删除
get: undefined, //取值的函数 默认是undefined
set: undefined //赋值的函数 默认是undefined
}
属性描述对象提供了6个元属性
| 属性 | 含义 |
|-----------------| ----------------|----------------|
| value | value
是该属性的属性值,默认为undefined。|
| writable | writable
是一个布尔值,表示属性值(value
)是否可改变(即是否可写),默认为true
。|
| enumerable | enumerable
是一个布尔值,表示该属性是否可遍历,默认为true
。如果设为false
,会使得某些操作(比如for...in
循环、Object.keys()
)跳过该属性。|
| configurable | configurable
是一个布尔值,表示可配置性,默认为true
。如果设为false
,将阻止某些操作改写该属性,比如无法删除该属性,也不得改变该属性的属性描述对象(value
属性除外)。也就是说,configurable
属性控制了属性描述对象的可写性。|
| get | get
是一个函数,表示该属性的取值函数(getter
),默认为undefined
。|
| set | set
是一个函数,表示该属性的存值函数(setter
),默认为undefined
。|
在一个描述对象中,它的内部控制属性的行为就有这些属性来控制。
可以通过 **Object.getOwnPropertyDescriptor()
**方法来获取这些属性。
Object.getOwnPropertyDescriptor()
Object.getOwnPropertyDescriptor()方法可以获取属性描述对象。它的第一个参数是目标对象,第二个参数是一个字符串,对应目标对象的某个属性名。
对象举例:
var obj={};
obj.name="小太阳";
obj.name="小胖子" ; //name是可被修改的
console.log(Object.getOwnPropertyDescriptor(obj,"name")) //获取属性描述对象的配置
数组举例:
var arr=["1","2","3"];
arr.length=5; //length可写,这里给写成5
console.log(arr.length)
console.log(Object.getOwnPropertyDescriptor(arr,"0"));
console.log(Object.getOwnPropertyDescriptor(arr,"length"));
console.log(Object.getOwnPropertyDescriptor(arr,"toString")) //不能用于继承的属性
注意:Object.getOwnPropertyDescriptor()
方法只能用于对象自身的属性,不能用于继承的属性
Object.prototype.propertyIsEnumerable()
判断某个属性是否可枚举(是否能被遍历的意思),可枚举性就是能被遍历的(实例自身的属性),不可枚举性就是不能被遍历的属性(继承来的,设置不可枚举的属性一律返回false)。
var obj={};
obj.a=123;
for(var key in obj){
console.log(key) //a 能被遍历出来的
console.log(obj[key]) //打印值
}
var obj={};
obj.a=123;
console.log(Object.propertyIsEnumerable("a")) //a可被枚举
console.log(Object.propertyIsEnumerable("toString")) //toString是继承下来的,不能被枚举
Object.defineProperty()
Object.defineProperty
方法允许通过属性描述对象,定义或修改一个属性,然后返回修改后的对象。
语法如下:
Object.defineProperty(object, propertyName, attributesObject)
//Object.defineProperty(属性所在的对象, 要修改的属性名, 属性描述对象)
Object.defineProperty
方法接受三个参数,依次如下。
名称 | 含义 |
---|---|
object | 属性所在的对象 |
propertyName | 字符串,表示属性名 |
attributesObjec | 属性描述对象 |
var obj = Object.defineProperty({},'name',{
value:'小太阳',
writable:false,
enumerable:true,
configurable:false
})
console.log(obj.name);//小太阳
obj.name = '小胖子';
console.log(obj.name);//小太阳 没被修改
delete obj.name;
console.log(obj.name);//小太阳还是能被获取 没被删除
上面代码中,
Object.defineProperty()
方法定义了obj.name
属性。由于属性描述对象的writable
属性为false
,所以obj.name
属性不可写。注意,这里的Object.defineProperty
方法的第一个参数是{}(一个新建的空对象)
,name
属性直接定义在这个空对象上面,然后返回这个对象,这是Object.defineProperty()
的常见用法。
如果属性已经存在,Object.defineProperty()
方法相当于更新该属性的属性描述对象。
Object.defineProperties()
如果一次性定义或修改多个属性,可以使用Object.defineProperties()
方法
var obj = Object.defineProperties({}, {
p1: { value: 123, enumerable: true },
p2: { value: 'abc', enumerable: true },
p3: {
get: function () {
return this.p1 + this.p2
},
enumerable:true,
configurable:true //可配置性
}
});
console.log(obj.p1);//123
console.log(obj.p2);//"abc"
console.log(obj.p3);//"123abc"
上面代码中,
Object.defineProperties()
同时定义了obj
对象的三个属性。调用obj.p3
的时候,每个属性描述对象中都有一个get
和set
取值函数,get
是获取值的,在这里获取p1
和p2
的value
,p3
属性定义了取值函数get
,即每次读取该属性,都会调用这个取值函数。
注意,一旦定义了取值函数get(或存值函数set),就不能将writable属性设为true,或者同时定义value属性,否则会报错
不能将writable定义为true,尽量不要写
var obj2=Object.defineProperty({},"name",{
writable:true,
get:function(){
return 456
}
})
定义了取值函数get,就不能同时定义value属性
var obj2=Object.defineProperty({},"name",{
value:"123",
get:function(){
return 456
}
})
Object.defineProperty()
和Object.defineProperties()
参数里面的属性描述对象,writable
、configurable
、enumerable
这三个属性的默认值都为false
。
var obj = {};
Object.defineProperty(obj, 'foo', {});
Object.getOwnPropertyDescriptor(obj, 'foo')
/*
{
configurable: false,
enumerable: false,
value: undefined,
writable: false,
}
*/
上面代码中,定义obj.foo
时用了一个空的属性描述对象,就可以看到各个元属性的默认值。obj
也遍历不出来。
元属性
value
value
属性是目标属性的值
var obj = {};
obj.p = 123;
console.log(Object.getOwnPropertyDescriptor(obj, 'p').value); //123
console.log(Object.defineProperty(obj, 'p', { value: 246 }));
console.log(obj.p )// 246
上面代码是通过
value
属性,读取或改写obj.p
的例子。
writable
writable
属性是一个布尔值,决定了目标属性的值(value)是否可以被改变。
var obj = {};
Object.defineProperty(obj, 'a', {
value: 37,
writable: false //不能被修改
});
console.log(obj.a )// 37
obj.a = 25; //不会被改变
console.log(obj.a )// 37
上面代码中,
obj.a
的writable
属性是false
。然后,改变obj.a
的值,不会有任何效果。
注意,正常模式下,对writable
为false
的属性赋值不会报错,只会默默失败。但是,严格模式下会报错,即使对a
属性重新赋予一个同样的值。
'use strict';
var obj = {};
Object.defineProperty(obj, 'a', {
value: 37,
writable: false
});
obj.a = 37;
上面代码是严格模式,对
obj.a
任何赋值行为都会报错。
如果原型对象的某个属性的writable
为false
,那么子对象将无法自定义这个属性。
var proto = Object.defineProperty({}, 'foo', {
value: 'a',
writable: false
});
var obj = Object.create(proto);
obj.foo = 'b'; //不会被修改
console.log(obj.foo) // 'a'
上面代码中,proto
是原型对象,它的foo
属性不可写。obj
对象继承proto
,也不可以再自定义这个属性了。如果是严格模式,这样做还会抛出一个错误。
但是,有一个规避方法,就是通过覆盖属性描述对象,绕过这个限制。原因是这种情况下,原型链会被完全忽视。
var proto = Object.defineProperty({}, 'foo', {
value: 'a',
writable: false
});
var obj = Object.create(proto);
Object.defineProperty(obj, 'foo', {
value: 'b'
});
console.log(obj.foo) // "b"
用obj覆盖了原型对象上的描述对象,这样就能修改了
enumerable
enumerable
(可遍历性)返回一个布尔值,表示目标属性是否可遍历。
如果一个属性的enumerable
为false
,下面三个操作不会取到该属性
for...in
循环Object.key
方法JSON.stringify
方法
var obj = {};
Object.defineProperty(obj, 'x', {
value: 123,
enumerable: false
});
obj.x // 123
for (var key in obj) {
console.log(key);
} //获取不到,啥也没有
console.log(Object.keys(obj)) // []
console.log(JSON.stringify(obj)) // "{}"
上面代码中,
obj.x
属性的enumerable
为false
,所以一般的遍历操作都无法获取该属性,使得它有点像“秘密”属性,但不是真正的私有属性,还是可以直接获取它的值。
注意,for...in
循环包括继承的属性,Object.keys
方法不包括继承的属性。如果需要获取对象自身的所有属性,不管是否可遍历,可以使用Object.getOwnPropertyNames
方法。
function A(){
this.name="小太阳";
}
function B(){
A.call(this);
}
var b=new B();
Object.defineProperty(b,"age",{
value:18,
enumrable:false //不可枚举的属性
})
console.log(b);
//用for...in遍历一下继承的属性能否被遍历出来
for(var key in b){
console.log(key)
}
console.log(Object.getOwnPropertyNames(b))
console.log(Object.keys(b))
通过for..in
循环不能遍历不可枚举属性,Object.keys(b)
也不能获取不可枚举属性age
,但是通过Object.getOwnPropertyNames
可以获取继承和不可枚举的属性。
另外,JSON.stringify
方法会排除enumerable为false
的属性,有时可以利用这一点。如果对象的 JSON
格式输出要排除某些属性,就可以把这些属性的enumerable
设为false
。
configurable
configurable
(可配置性)返回一个布尔值,决定了是否可以修改属性描述对象。也就是说,configurable
为false
时,value
、writable
、enumerable
和configurable
都不能被修改了。
var obj = Object.defineProperty({}, 'p', {
value: 1,
writable: false,
enumerable: false,
configurable: false //不能被修改
});
Object.defineProperty(obj, 'p', {value: 2})
// TypeError: Cannot redefine property: p
Object.defineProperty(obj, 'p', {writable: true})
// TypeError: Cannot redefine property: p
Object.defineProperty(obj, 'p', {enumerable: true})
// TypeError: Cannot redefine property: p
Object.defineProperty(obj, 'p', {configurable: true})
// TypeError: Cannot redefine property: p
上面代码中,obj.p
的configurable
为false
。改动value
、writable
、enumerable
、configurable
,结果都报错。
注意,writable
只有在false
改为true
会报错,true
改为false
是允许
var obj = Object.defineProperty({}, 'p', {
writable: true,
configurable: false
});
console.log(Object.defineProperty(obj, 'p', {writable: false}));
// 修改成功
至于value
,只要writable
和configurable
有一个为true
,就允许改动。
var o1 = Object.defineProperty({}, 'p', {
value: 1,
writable: true,
configurable: false
});
Object.defineProperty(o1, 'p', {value: 2})
// 修改成功
console.log(o1);
var o2 = Object.defineProperty({}, 'p', {
value: 1,
writable: false,
configurable: true
});
Object.defineProperty(o2, 'p', {value: 5})
console.log(o2);
可配置性决定了目标属性是否可以被删除(delete)。
var obj = Object.defineProperties({}, {
p1: { value: 1, configurable: true },
p2: { value: 2, configurable: false }
});
delete obj.p1 // true
delete obj.p2 // false
obj.p1 // undefined
obj.p2 // 2
上面代码中,obj.p1
的configurable
是true
,所以可以被删除,obj.p2
就无法删除
存储器
Object.defineProperty() 里面有个get和set来对我们当前的属性进行存储。很多高级应用中都用这个方法
- 除了直接定义以外,属性还可以用存取器定义。其中,存值函数称为setter,使用属性描述对象的set属性;取值函数称为getter,使用属性描述对象的get属性
- 一旦对目标属性定义了存取器,那么存取的时候,都将执行对应的函数。利用这个功能,可以实现许多高级特性,比如某个属性禁止赋值
var obj = Object.defineProperty({},'p',{
get:function(){
return 'getter';
},
set:function(value){
console.log('setter:'+value);//"setter:123"
}
})
console.log(obj.p )//"getter"
obj.p = 123 ;
上面代码中,
obj.p
定义了get
和set
属性。obj.p
取值时,就会调用get
,赋值时,就会调用set
。
用这种方法我们可以设置某个对象中的某个属性是否能赋值
var obj = Object.defineProperty({},'p',{
get:function(){
return 'getter';
},
set:function(value){
// console.log('setter:'+value);//"setter:123"
return;
}
})
console.log(obj.p )//"getter"
obj.p = 123 ;
上述代码中
set
的代码不执行,所以下面obj.p=123
这个赋值不成功。
JavaScript中除了提供上述代码的写法,还提供了一种简便写法。 get是取值函数不能接收参数,set是存值函数,要接收一个参数仅能接收一个参数。
var obj={
get p(){ //这是取值函数不能传入参数
return "getter";
},
set p(value){ //只能接收一个参数
console.log("setter:"+value)
}
}
console.log(obj.p);
obj.p=123;
存储器
get
和set
往往用于属性值依赖,一个属性中有value
属性值,在这里面会针对于当前对象中的某个属性值进行操作。
存取器往往用于,属性的值依赖对象内部数据的场合
var obj = {
$n : 5,
get next(){
return this.$n++;
},
set next(value){
if(value >= this.$n){
this.$n = value;
}else{
throw new Error('新的值必须大于当前值');
}
}
};
console.log(obj.next) //5
obj.next = 10;
console.log(obj.next) //10
obj.next = 5;
console.log(obj.next) //10
// Uncaught Error: 新的值必须大于当前值
上面代码中,next
属性的存值函数和取值函数,都必须依赖内部属性$n
,一开始值存储在$n
中,next
和$n
做比较,如果赋予的值大于$n
代码将依次执行,如果小于则抛出错误。
拷贝
在JavaScript中拷贝分为深拷贝与浅拷贝
次数回顾一下JavaScript中的数据类型,数据类型也分为两种数据类型,一种是基本数据类型,一种的引用的数据类型。
基本数据类型:按值来传递的
经典例子:
var a=1;
var b=a;
b=200;
console.log(a);
console.log(b);
上述,
b
的值修改为200,当修改了拷贝之后变量中的值,不会影响原来数据类型中的的数据。那么这种方案称为深拷贝。
引用数据类型:引用类型按引用传递 例子:
var arr=[1,2,3,4];
var newArr=arr;
newArr.push(10);
console.log(newArr);
console.log(arr)
当该变量拷贝了之后这个对象中的数据的时候,紧接着原始对象中的数据也会发生改变,这种方案叫浅拷贝。
拷贝完这个对象一旦发生改变,原始对象中数据也跟着发生改变的叫浅拷贝,相反则是深拷贝(有影响的叫浅拷贝,没影响的叫深拷贝)。
浅拷贝
var obj={
name:"小太阳",
age:18,
hobby:"打球",
friend:{
name:"小胖子",
age:15
}
}
function shadowCopy(toObject,fromObj){
for(var key in fromObj){
toObject[key]=fromObj[key] //这样就是直接赋值了
}
return toObject;
}
var newObj = shadowCopy({},obj);
console.log(newObj) //此时原始的对象已经拷贝过来了
//改变新的对象中age
newObj.age=20;
newObj.friend.name="小明";
console.log(newObj);
console.log(obj)
name
和age
都是基本的数据类型。hobby爱好
,friend朋友
对应一个对象,朋友有名字和年龄,现在把obj
中的数据拷贝到新的对象中,把obj
中的每一个属性直接复制到新的数据中,定制一个函数shadowCopy
,传入两个参数,第一个参数toObject
,第二个参数fromObj
。从fromObj
对象中拷贝到toObject
对象中。调用ShadowCopy
的时候传入一个空对象,把obj
对象传递进去,然后return
一个拷贝后的对象。var newObj
是一个储存拷贝过后的新对象。ShadowCopy
对象函数内部把拷贝之后的对象return toObj
返回出来。friend
是一个对象属于引用类型,如果想实现一个浅拷贝,一旦newObj
该变了friend
中的name
。通过for..in
循环在内部toObj[key]=fromObj[key]
直接复制。
在newObj
中age
发生了改变,在原始对象obj
中age
却没有发生改变。因为这些都是基础的数据类型。但是friend
中的name
在新的对象和原始对象中都有发生变化,这是因为friend
是一个引用类型。安引用地址进行传递,改变了新的数据地址把原始数据地址也改变了。
由此可见浅拷贝不是直接赋值,浅拷贝是新建一个对象,将原对象的属性一一复制,复制的是值,而不是引用,引用是按地址访问的,然后在给引用地址赋值。浅拷贝的复制只是复制了第一层的属性,并没有递归所有的值复制归来(仅仅复制了obj中的name一类的属性,但是friend把一个内存地址给复制过去)。一层一层进行遍历就会存在递归。
深拷贝
深拷贝就是对目标的完全拷贝,不像浅拷贝只复制了一层的引用。只要进行了深拷贝,原来的对象和现在的对象就是独立的再无关联,谁也不影响谁。
var obj={
name:"小太阳",
age:18,
hobby:["打球","跑步"],
friend:{
name:"小胖子",
age:15,
friend:{
name:"小彩",
age:12
}
}
}
function deepCopy(to,fromA){
// 遍历fromA对象所有的属性,拷贝到to中
for(var key in fromA){
// 不遍历原型链上的属性
if(fromA.hasOwnProperty(key)){
// 如果值是对象并且有值,接着遍历对象
if(fromA[key] && typeof fromA[key] === "object"){ //是否是一个对象,并且有值
//fromA[key]的构造器函数是否等于Array,如果是一个Array就返回一个数组,如果不是就返回一个对象
// 然后给to[key]赋值
to[key]=fromA[key].constructor===Array ? [] : {};// 区分一般对象还是数组区分好了就给to[key]赋值
//继续递归再调用deepCopy(to.[key],fromA[key]) 遇到对象再深入的遍历,最终赋值给to[key]
to[key] = deepCopy(to[key],fromA[key]);
}else{
// 如果不是直接赋值
to[key]=fromA[key]
}
}
}
return to;
}
var newObj = deepCopy({},obj);
newObj.friend.name="小明"; //在新对象中把朋友的名字改掉
console.log(newObj) //这里朋友的名字改变了
console.log(obj) //这里朋友的名字 没有改变
原始数据没被改变,新的数据改变了。
hasOwnProperty
属性用来过滤掉继承的属性。