目录
1.对象
1.1 类型
六种语言类型:
- string
- number
- boolean
- null
- undefined
- object
但前五种本身并不是对象。
内置对象:
- String
- Number
- Boolean
- Object
- Function
- Array
- Date
- RegExp
- Error
可以当作构造函数来用,用new构造一个对应子类型的对象。
1.2 对象拷贝
function anotherFunction(){
}
var anotherObject = {
c:true
}
var anotherArray= []
var myObject = {
a:2,
b:anotherObject,
c:anotherArray,
d:anotherFunction
}
anotherArray.push(anotherObject,myObject)
如果是浅拷贝,拷贝出来的新对象中的b、c、d三个属性只是三个引用,和旧对象中的b、c、d应用的对象是一样的;
如果是深拷贝,除了复制myObject外还会复制anotherObject和anotherArray,但anotherArray又引用了anotherObject和myObject,又需要复制,从而导致死循环。
有一种JSON安全的复制方法
var newObj = JSON.parse(JSON.stringify(someObj))
ES6定义了Object.assign(..)
方法来实现浅拷贝:
var newObj = Object.assign({
},myObject)
newObj.a //2
newObj.b === anotherObject //true
newObj.c === anotherArray //true
newObj === anotherFunction //true
1.3 属性描述符
从ES5开始,所有的属性都具备属性描述符。
可以用Object.defineProperty()
来添加一个新属性或修改已有属性。
var obj = {
}
Object.defineProperty(obj,'a',{
value:2,
writable:true,
configurable:true,
enumerable:true
})
console.log(obj.a) //2
- writable决定是否可以修改属性的值
- configurable,属性可配置,则可以使用
defineProperty()
来修改属性描述符。 - enumerable,描述属性是否会出现在对象的属性枚举中,如for…in循环,若enumerable设置为false,则不会出现在枚举中,但仍然可以正常访问。
- getter和setter:后面再说
1.4 Getter和Setter
getter是要给隐藏函数,会在获取属性值时调用。
setter会在设置属性值时调用。
var myObject = {
// 给a定义一个getter
get a(){
return 2
}
}
Object.defineProperty(myObject,'b',{
//给b设置要给getter
get:function(){
return this.a*2},
enumerable:true
})
console.log(myObject.a)
console.log(myObject.b)
myObject.a = 3
console.log(myObject.a)
两种方法,一种是get定义,一种是defineProperty()定义,由于只给a定义了getter,所以对a的值进行设置时set操作会忽略赋值操作。
var myObject = {
// 给a定义一个getter
get a(){
return this.r
},
set a(val){
this.r = val * 2
}
}
myObject.a = 2
console.log(myObject.a)
1.5 存在性
可以在不访问属性值的情况下判断对象中是否存在这个属性:
var myObject = {
a:2
}
('a' in myObject) //true
('b' in myObject) //false
myObjcet.hasOwnProperty('a') //true
myObject.hasOwnProperty('b') //false
-
in
操作符会检查属性是否在对象及[[Prototype]]原型链中。实际上它检查的是某个属性名是否存在:console.log(4 in [2,4,6])
这里会打印false,因为数组中属性名是0、1、2,没有4。
-
hasOwnProperty()
只会检查属性是否在myObject对象中,不会检查原型链。
判断是否可枚举:
propertyIsEnumerable()
会检查给定的属性名是否存在于对象中并满足enumerable:trueObject.keys()
返回包含所有可枚举属性的数组Object.getOwnPropertyNames()
返回包含所有属性的数组,无论是否可枚举
1.6 遍历
除了本身的for循环遍历外,ES5增加了一些数组的赋值迭代器,如forEach()
、every()
、some()
,而它们都是遍历数组下标从而找到数组的值,ES6增加了for..of
语法,可以直接遍历值。
var myArray = [1,2,3]
for(var v of myArray){
console.log(v)
}
它首先会向被访问对象请求一个迭代器对象,然后通过迭代器对象的next()
方法来遍历所有的返回值。
数组有内置的@@iterator
,可以用它来手动遍历数组:
var myArray = [1,2,3]
var it = myArray[Symbol.iterator]()
it.next(); {
value:1,done:false}
it.next(); {
value:2,done:false}
it.next(); {
value:3,done:false}
it.next(); {
done:false}
@@iterator
是一个返回迭代器对象的函数。
for...of
可以遍历数组、对象等等,它会寻找内置的或自定义的@@iterator
对象并调用它的next()方法来遍历。
2.混合对象“类”
JS中只有对象,不存在可以被实例化的类。一个对象不会被复制到其他对象,它们会被关联起来。
但也有一个方法来模拟类的复制行为,称为混入
2.1 显式混入
function mixin(sourceObj,targetObj){
for(var key in sourceObj){
if(!(key in targetObj)){
targetObj[key] = sourceObj[key]
}
}
return targetObj
}
var Vehicle = {
engines:1,
ignition:function(){
console.log("Turning on my engine.")
},
drive:function(){
this.ignition()
console.log("Steering and moving forward!")
}
}
var Car = mixin(Vehichle,{
wheels:4,
drive:function(){
Vehicle.drive.call(this)
console.log("Rolling on all" + this.wheels + "wheels")
}
})
现在Car中就有了一份Vehicle属性和函数的副本了。
如果直接执行Vehicle.drive(),函数调用中的this会被绑定到Vehicle对象而不是Car对象,这和我们预期不符,因此要用.call(this)
来确保drive()在Car对象的上下文中执行。
虽然复制后总体上两者已基本分离,但实际上还是有一些巧妙的方法可以影响到对象,比如引用同一个对象/数组。
2.2 隐式混入
var Something = {
cool:function(){
this.greeting = 'hello world'
this.count = this.count?this.count+1:1
}
}
Something.cool()
Something.greeting //'hello world'
Something.count // 1
var Another = {
cool:function(){
//隐式把Someting混入Another
Something.cool.call(this)
}
}
Another.cool()
Another.greeting //'hello world'
Another.count //1
这里我们通过this绑定隐式地把Something混入了Another,最终结果是Something.cool()中的赋值操作都会应用在Another对象上而不是Something对象上(最后一个count不是2而是1)。
2.3 小结
在JS中类意味着复制,它并不会像传统的类那样自动创建对象的复制,需要我们手动来进行复制,而混入可以用来模拟类的复制行为。但在JS中模拟类的行为可能会产生很多隐患,因此不常使用。