原生js的数据属性,访问器属性
var foo = {}
Object.defineProperty(foo, 'name', {
configurable: true,
enumerable: true,
writable: true,
value: 'rose'
})
Object.defineProperties(foo, {
sex: {
configurable: true,
enumerable: true,
writable: true,
value: 'man'
},
time: {
configurable: true,
enumerable: true,
writable: true,
value: 'haha'
}
})
// 在这儿只指定get,则属性不能写入数据,只指定set,属性不能读取数据
Object.defineProperty(foo, 'age', { // 注意与es6 的proxy作区分
get: function() {
return this._age
},
set: function(value) {
this._age = value
}
})
console.log(foo)
foo.age = 30
console.log(foo.age, foo)
// 读取属性的特性
var descriptor = Object.getOwnPropertyDescriptor(foo, 'name')
console.log(descriptor)
es6 proxy(注意与上面defineProperty相对比)
var origin = {
name: 'rose'
}
var handler = {
get: function(target, key) {
return target[key]
},
set: function(target, key, value) {
target[key] = value
}
}
var proxy1 = new Proxy(origin, handler)
proxy1.age = 18
console.log(origin)
创建对象的方式
1. 工厂模式(不足:其没有解决对象的识别问题,即不知道究竟是哪个对象)
function creatPersonObject(name, age, sex) {
var o = new Object()
o.name = name
o.age = age
o.sex = sex
return o
}
console.log(creatPersonObject('rose', 22, 'man'))
2.构造函数模式(不足:在实例化时,如果遇到function都会去实例化function,造成每个实例化后的function不同,没有必要)
function Person(name, age, sex) {
this.name = name
this.age = age
this.sex = sex
this.go = function() {
console.log('go')
}
}
// new 操作符的步骤: 1.创建新的对象,2.将构造函数的作用域赋值给新的对象 3. 执行构造函数 4. 返回新对象
var person1 = new Person('kobe', 23, 'man')
var person2 = new Person('durant', 21, 'man')
console.log(person1, person1 instanceof Person, person1.go == person2.go)
3.原型模式
function Man(name, age) {
this.name = name
this.age = age
}
Man.prototype.go = function() {
console.log('go')
}
var man1 = new Man('qw', 12)
var man2 = new Man('er', 14)
console.log(man1, man1.go == man2.go)
3.1 理解原型对象
/**
* 构造函数拥有一个prototype属性指向构造函数的原型对象
* 构造函数的原型对象拥有一个constructor属性指向构造函数,【注】该属性为共享,故实例也拥有constructor属性,且指向构造函数
* 实例的[[prototype]]指向构造函数的原型对象
* isPrototypeOf()
* Object.getPrototypeOf()
* hasOwnProperty() 判断是属于原型对象的属性,还是属于实例的属性,属于实例的属性,返回true
* in操作符,单独使用时,不管属性是否为原型属性还是实例属性,若存在,都为true
* for...in 同上,还要加一个条件:属性的[[Enumerable]]标记为true,可枚举
* Object.keys() // 返回对象所有支持枚举的属性组成的数组
* Object.getOwnPropertyNames() // 返回所有属性,不管属性可否枚举
*/
function People(name) {
this.name = name
}
People.prototype.say = function() {
console.log('hello')
}
var people1 = new People('AA')
console.log(People.prototype.isPrototypeOf(people1)) // true
console.log(Object.getPrototypeOf(people1)) // 获取people的原型对象
console.log(people1.constructor) // People
console.log(people1.hasOwnProperty('say')) // false
console.log(people1.hasOwnProperty('name')) // true
console.log('name' in people1) // true
console.log('say' in people1) // true
/*
如果原型对象以对象字面量的形式书写,则原型对象的constructor属性将不再指向构造函数,因为此时prototype相当于被重写了
*/
function Foo() {}
Foo.prototype = {
name: 'aa',
age: 18
}
console.log(Foo.prototype.constructor) // 此时为Object的构造函数,而非Foo
var foo = new Foo()
console.log(foo.constructor) // Object
// 如果此时constructor的值很重要,则可以将其进行手动修复
Foo.prototype.constructor = Foo
console.log(Foo.prototype.constructor) // Foo
console.log(foo.constructor) // Foo
// 但是通过上面方式,constructor是可以被枚举出来的,下面的做法可以避免
Object.defineProperty(Foo.prototype, 'constructor', {
enumerable: false,
value: Foo
})
// 重写原型可能造成的问题:如果重写发生在实例化之后,则实例的[[prototype]]不会指向重写的原型;一定要是重写!!!
function Foo2() {}
var foo2 = new Foo2()
Foo2.prototype = {
name: 'dd'
}
console.log(foo2.name) // undefined
4.组合使用构造函数与原型模式,上面已经有了
5.动态原型模式(即在构造函数中初始化原型)
function Foo3(name) {
this.name = name
if (typeof this.say !== 'function') {
Foo3.prototype.say = function() {
console.log('say!')
}
}
}
var foo3 = new Foo3('ss')
console.log(foo3, foo3 instanceof Foo3) // {}, true
6.寄生构造函数模式(有点类似于工厂模式, 不能用instanceof来进行判断,一般不用这个方法)
function CreateFoo4(name) {
var o = new Object()
o.name = name
o.say = function() {
console.log(this.name)
}
return o
}
var foo4 = new CreateFoo4('sd')
console.log(foo4, foo4 instanceof CreateFoo4) // {}, false
7.稳妥构造函数模式(不使用this和new操作符,其主要作用就是原始传入的数据,不能通过对象直接访问,只能通过函数去获取,保证数据安全,不适合使用instanceof)
function Foo5(name, age) {
var o = new Object()
o.getName = function() {
return name
}
o.getAge = function() {
return age
}
return o
}
var foo5 = Foo5('asd', 22)
console.log(foo5)