前提:默认已经熟悉js原型链。
#####1.对象设置get 和set的方法
方法1:
var obj = {
name: '张三',
get name2() {
// todo...
return this.name + 'a';
},
set name2(name) {
// todo...
this.name = name;
},
set nameSet(name) {
// todo...
this.name = name;
}
};
同一个名字,即可以设置set,也可以设置get,
也可以只设置一个,重复设置前者会被后者覆盖。
console.log(obj.name2); //张三a
obj.name2= '李四';
console.log(obj.name2); //李四a
obj.nameSet = '王五';
console.log(obj.name2); //王五a
方法2:
var obj = {
name: '张三'
};
obj.__defineGetter__('name2', function() {
return this.name + 'a';
});
obj.__defineSetter__('name2', function(name) {
this.name = name;
});
obj.__defineSetter__('nameSet', function(name) {
this.name = name;
});
console.log(obj.name2); //张三a
obj.name2= '李四';
console.log(obj.name2); //李四a
obj.nameSet = '王五';
console.log(obj.name2); //王五a
不说你也应该知道不推荐,Object.defineProperty之前的
方法,属于遗留的方法!
方法3:
var obj = {
name: '张三'
};
Object.defineProperty(obj, 'name2', {
get(){
return this.name
},
set(name){
this.name = name + 'a'
}
})
console.log(obj.name2); //张三
obj.name2= '李四';
console.log(obj.name2); //李四a
推荐方法!
不支持Object.defineProperty方法的浏览器中,
无法修改configurable和enumerable描述符
避免错误:
var obj = {
get name2() {
// todo...
return this.name2 + 'a';
},
set name2(name) {
// todo...
this.name2 = name;
}
};
console.log(obj.name2)
//报错,超过最大调用堆栈大小,
//此时成为了一个无限调用自身的递归函数
#####2.Object.assign(target目标对象, sources源对象) 合并对象使用
const o1 = { a: 1 };
const o2 = { b: 2 };
const o3 = { c: 3 };
const obj = Object.assign(o1, o2, o3);
console.log(obj); // { a: 1, b: 2, c: 3 }
console.log(o1); // { a: 1, b: 2, c: 3 }, 注意目标对象自身也会改变。
#####3.Object.create(proto[, propertiesObject]) 创建一个新的对象,这个对象拥有自定义原型对象proto
//注意:如果propertiesObject参数没有constructor属性,那么
//创建的新对象的原型对象上也不会有。
var a = { a: 1, b: 2 }
var b = Object.create(a, {
c:{
value: 3,
// 能否通过delete删除,能否修改特性,能否把属性修改为访问器属性
configurable: true,
//表示是否可以通过for in 遍历出来,也就是常说的可枚举性
enumerable: true,
//能否修改属性的值
writable: true
}
})
//propertiesObject对应Object.defineProperties()的第二个参数
//注意:propertiesObject参数添加的属性是添加到创建出来的新对象b,
//而不是作为原型使用的对象a
console.log(b) // { c: 3 }
console.log(b.__proto__) // { a: 1, b: 2 }
//类似原生写法;
function create(proto) {
function f(){}
f.prototype = obj
return new f()
}
function create(proto){
let o = new Object
o.__proto__ = proto
return o
}
#####4.Object.defineProperty 处理单个属性 #####Object.defineProperties 批量处理属性 产生的效果包括副作用完全一致,都作用于象本身属性,不涉及原型属性。
var obj = {};
Object.defineProperties(obj, {
'property1': {
//这个属性与[get,set]只能二选一,如果同时出现,后者会覆盖前者
value: true,
// 能否通过delete删除,能否修改特性,能否把属性修改为访问器属性
configurable: true,
//表示是否可以通过for in 遍历出来,也就是常说的可枚举性
enumerable: true,
//能否修改属性的值
writable: true
},
'property2': {
value: 'Hello',
writable: false
}
});
Object.defineProperty书写方式下会提到
#####5.Object.getOwnPropertyDescriptor (obj,name)获取单个属性的描述符 #####Object.getOwnPropertyDescriptors(obj)获取所有属性的描述符
var obj = {a:1};
var o ={a:1};
Object.defineProperties(obj, {
'property1': {
value: true,
configurable: true,
enumerable: true,
writable: true
},
'property2': {
value: 'Hello'
}
});
Object.defineProperty(obj, 'a', {
get(){
return this.a
},
set(name){
this.a = name + 'a'
}
})
console.log(Object.getOwnPropertyDescriptor(obj, 'property1'))
console.log(Object.getOwnPropertyDescriptor(obj, 'property2'))
console.log(Object.getOwnPropertyDescriptor(obj, 'a'))
console.log(Object.getOwnPropertyDescriptor(o, 'a'))
{ value: true,
writable: true,
enumerable: true,
configurable: true
}
{ value: 'Hello',
writable: false,
enumerable: false,
configurable: false
}
结论:如果是新定义的属性【defineProperty和defineProperties】,
writable | enumerable | configurable 都会变成false
{ get: [Function: get],
set: [Function: set],
enumerable: true,
configurable: true
}
结论:如果修改已经存在的的属性【defineProperty和defineProperties】,
writable:只要设置get和set其中之一,这个描述符就没有意义了,只设置get
不设置set其实就是把writable设置成false的效果。
enumerable | configurable 默认都是true
{ value: 1, writable: true, enumerable: true, configurable: true }
结论:常规对象的默认属性的描述符,默认值都是true
#####6.【for...in ,Object.keys(obj) ,Object.getOwnPropertyNames(obj) ,obj.hasOwnProperty(prop),obj.propertyIsEnumerable(name)】
6-1: for...in 无论是自身属性还是原型属性,只要是可枚举的,我都给你干出来。 遍历的顺序是,优先处理数字属性名(1 和 '1'都可以,但必须是正整数)并且排序,其次,不是数字的,按照书写顺序进行排列。
var a = {f:1,s:2,w:3,0:2,3:4,2:3,7:3,1:3,'-5':2,'-1':2}
for(let i in a){console.log(i)}
//0, 1, 2, 3, 7, f, s, w, -5, -1
注意:在class的内部所有定义的原型方法,默认都是
不可枚举的,且不可声明属性,只能声明方法。
class P1{
say(){}
go(){}
}
function P2(){}
P2.prototype = {
say(){},
go(){}
}
var a = new P1()
var b = new P2()
Object.assign(a, {a:1,b:2,c:3})
Object.assign(b, {a:1,b:2,c:3})
for(let i in a){console.log(i)} // a, b, c
for(let i in b){console.log(i)} // a, b, c, say, go
6-2: Object.keys(obj) 我没有for...in那么强大,我只把对象自身属性且可枚举的包装成数组返回给你。遍历的顺序和for...in一致
var a = {a:1}
a.__proto__ = {d:4,e:5}
Object.defineProperties(a, {
b:{
value: 2,
enumerable: false
},
c:{
value: 3,
enumerable: false
}
})
Object.keys(a) // ['a']
6-3:Object.getOwnPropertyNames(obj) 我与for...in和Object.keys(obj)不同,我会把对象所有自身属性的包装成数组返回给你,包括不可枚举的。遍历的顺序和for...in一致
var a = {a:1}
a.__proto__ = {d:4,e:5}
Object.defineProperties(a, {
b:{
value: 2,
enumerable: false
},
c:{
value: 3,
enumerable: false
}
})
Object.getOwnPropertyNames(a) // ["a", "b", "c"]
6-4:obj.hasOwnProperty(prop) 我会判断当前的属性是不是对象自身属性,包括不可枚举。属性值是null和undefined也不影响。
var a = {a:1}
a.__proto__ = {d:4,e:5}
Object.defineProperties(a, {
b:{
value: null,
enumerable: false
},
c:{
value: undefined,
enumerable: false
}
})
a.hasOwnProperty('a') //true
a.hasOwnProperty('b') //true
a.hasOwnProperty('c') //true
6-5:obj.propertyIsEnumerable(name) 我会返回一个布尔值,以判断当时属性是不是可枚举的,原型属性全部返回false
var a = {a1:1,a2:2,a3:3}
var b = Object.create(a)
Object.assign(b, {b1:1,b2:2,b3:3})
for(let i in b){
console.log(`${i}: ${b.propertyIsEnumerable(i)}`)
}
b1: true
b2: true
b3: true
a1: false
a2: false
a3: false
6-6:如果你只要获取对象自身可枚举属性 1. Object.keys 2. for...in循环(使用hasOwnProperty()方法过滤掉原型属性)。 3. for...in循环(使用propertyIsEnumerable()方法过滤掉原型属性)。 4. Object.getOwnPropertyNames(a)(使用propertyIsEnumerable()方法过滤掉原型属性) #####7.Object.getPrototypeOf(a),概念很简单,就是返回当前对象的原型对象
var a = {}
a.__proto__ === Object.getPrototypeOf(a) // true
Object.getPrototypeOf 与 __proto__ 的读作用一致
__proto__ 是可读可写的。
#####8. Object.setPrototypeOf(obj, prototype),概念也很简单,重新设置一个对象的原型对象
但是副作用很复杂,影响性能,参考 MDN。
使用的场景:
首先回忆Object.create()的用法
var a = {a1:1,a2:2,a3:3}
var b = Object.create(a)
得到了一个空对象b,且该对象的原型对象为a
我现在有两个已知对象
var a = {a1:1,a2:2,a3:3}
var b = {b1:1,b2:2,b3:3}
如果我想让b继承自a,也就是让a成为b的原型对象,
就可以用Object.setPrototypeOf(b, a)
等同于
b.__proto__ = a
如果对深层次的影响不熟悉的话,还是要避免使用,起码MDN不推荐,但是为啥还要定义出来呢? 估计是因为之前各大浏览器厂商都自己实现了__ proto __,但这并不是ECMA规范,之后ECMA感觉反正大家都这么用了,干脆定义一下吧,应该是这么回事,我瞎编的。
#####9.obj.isPrototypeOf(),方法用于测试一个对象是否存在于另一个对象的原型链上。 功能好像和instanceof一样,但是有区别。
obj instanceof function
instanceof 是操作符,
左边是对象类型,右边是 构造函数(必须是函数)
当此表达式运行时,会先寻找function,然后观察function
的prototype是否存在于obj的原型链上。
obj_proto.isPrototypeOf(obj)
而isPrototypeOf是对象的方法,接受的参数也是对象,
当此表达式运行时,根本不会关心obj2
是不是哪个function的prototype属性,
只关心obj_proto对象本身是不是存在于obj的原型链上。
但是isPrototypeOf又能解决什么问题呢?
现在我们使用Object.create 实现对象的直接继承。
他的原理写法如下:
var a = {name:'张三', age:18}
function create(obj) {
function f(){}
f.prototype = obj
return new f()
}
var b = create(a)
那么好了,现在我明知道b的原型对象是a,
但是我该怎么使用instanceof判断呢?
是这样吗?
console.log(b instanceof a)
// Right-hand side of 'instanceof' is not callable
明显是这样
console.log(a.isPrototypeOf(b)) //true
#####*其他无关紧要的:对象与集合
现代的集合一般被定义为:由一个或多个确定的元素所构成的整体。
集合的特性 1.确定性: 给定一个集合,任给一个元素,该元素或者属于或者不属于该集合,二者必居其一,不允许有模棱两可的情况出现。 2.互异性: 一个集合中,任何两个元素都认为是不相同的,即每个元素只能出现一次。 3.无序性: 一个集合中,每个元素的地位都是相同的,元素之间是无序的。
对象的特性 1.获取一个对象的属性,如果存在便返回对应的值,如果不存在,并不会抛出错误,而是返回一个undefined,贴合集合特性中的确定性。 2.对象中的属性名不能有重复,如果定义了重复的属性名,后者会覆盖前者,符合集合特性中的互异性。 3.对象属性没有顺序符合集合特性中的无序性。
集合与对象的写法 集合:N = {0,1,2,3,…} 【自然数集合N】 对象:var a = {name: '张三', age: 13}
集合 | 罗素 村里的理发师说:只给村子里那些不给自己理发的人理发。 L = { 不给自己理发的人 } L补 = { 给自己理发的人 } 村里的人 = L + L补 = 1 = 无穷
如果理发师属于集合L,他就要给自己理发,那么他就和自己承诺的“只给那些不给自己理发的人理发”相违背。 如果理发师属于集合L补,他就不能给自己理发,那么他就和自己承诺的“不给那些给自己理发的人理发”相违背。
理发师要不要给自己理发呢?
集合S由一切不属于自身的元素组成,那么S到底属不属于S?它既不能属于,又不能不属于。
在《算术基本法则》中,弗雷格尝试用集合概念来定义数,并自认为这一任务已大致完成。然而就在这时候,他收到了罗素的一封信,其中的主要内容是所谓的"罗素悖论",这一悖论对弗雷格的整个事业是一个毁灭性的打击。用弗雷格自己的话来说,"在工作已经结束时,自己建造的大厦的一块主要基石却动摇了,对于一个科学家来说,没有比这更让人沮丧了" 。
哥德尔的不完备性定理解救了集合当时的尴尬局面。