深入JS面向对象

2021/12/13 JavaScript属性描述符

# 面向对象是现实的抽象方式

# 创建对象

# 通过new Object()

var obj = new Object()
obj.name = "okarin"
obj.age = 18
obj.height = 1.88
obj.running = function(){
    console.log(this.name + "在跑步")
}
1
2
3
4
5
6
7

# 通过字面量

var info = {
    name:"okarin",
    age:18,
    height:1.88,
    eating:function(){
        console.log(this.name + "在吃东西")
    }
}
1
2
3
4
5
6
7
8

# 创建多个对象

# 工厂模式

function createPerson(name,age,address){
    var p  ={}
    p.name = name
    p.age = age
    p.address = address
    p.eating = function(){
        console.log(this.name + "在吃东西~")
    }

    return p
}

var p1 = createPerson("张三",18,"成都")
var p2 = createPerson("李四",20,"上海")
1
2
3
4
5
6
7
8
9
10
11
12
13
14

工厂模式的缺点:获取不到对象最真实的类型

# 构造函数

构造函数是在创建对象时会调用的函数。

# JS中的构造函数

当通过 new关键字 调用一个函数,那么这个一个函数就是一个构造函数了。

# 构造函数创建对象
function Person(name,age,address){
    this.name = name
    this.age = age
    this.address = address
    this.eating = function(){
        console.log(this.name + "在吃东西~")
    }
}

var p1 = new Person("张三",18,"成都")

console.log(p1)

//Preson { name: '张三', age: 18, address: '成都', eating: [Function] }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
  1. 构造函数名字首字母要大写
  2. 构造函数不需要return 就可以返回结果
  3. 调用构造函数 必须使用 new
  4. 只要调用函数就创建一个对象
  5. 属性和方法前面必须添加 this
# 构造函数的缺点

当对象值是函数时,创建的每一个对象的函数是不一样的,这样如果创建很多个对象,也会创建多个新的函数,这是不必要的。

# 原型加构造函数

function Person(name,age,address){
    this.name = name
    this.age = age
    this.address = address
}

Person.prototype.eating = function(){
        console.log(this.name + "在吃东西~")
    }

var p1 = new Person("张三",18,"成都")

console.log(p1)
console.log(p1.__proto__)

//Person { name: '张三', age: 18, address: '成都' }
//Person { eating: [Function] }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17

# 对对象属性的操作

# Object.defineProperty

Object.defineProperty() 方法会直接在一个对象上定义一个新的属性,或者修改一个对象的现有属性,并返回此对象。

var obj = {
    name:"okarin",
    age:18,
    eating:function(){
        console.log(this.name + "在吃东西")
    }
}

Object.defineProperty(obj,"name",属性描述符) //修改
Object.defineProperty(obj,"like ",属性描述符) //添加
1
2
3
4
5
6
7
8
9
10

添加的属性默认是不可枚举的

var obj = {
    name:"okarin",
    age:18,
    eating:function(){
        console.log(this.name + "在吃东西")
    }
}

Object.defineProperty(obj,"height",{
    value:1.88
})

for(var key in obj){
    console.log(key , ":",obj[key])  //这时的height是不可枚举的 不会打印height
}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16

# 属性描述符分类

# 数据属性描述符

var obj = {
    name:"okarin",
    age:18,
    eating:function(){
        console.log(this.name + "在吃东西")
    }
}
//数据属性描述符
Object.defineProperty(obj,"address",{
    //配置
    value:"chengdu", //默认值 undefined
    configurable:false,//属性是否是可配置的 默认 false 不能删除也不能更改 
    enumerable:false,//属性是否是可枚举的 默认 false 不能枚举 也不能遍历
    writable:false,//属性是否是可以写入的 默认 false 不能写入 
})
delete obj.age
console.log(obj.age) //undefined

delete obj.address
console.log(obj.address)//chengdu
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
# 存取属性描述符

var obj = {
    name:"okarin",
    age:18,
    _address:"chengdu",
    eating:function(){
        console.log(this.name + "在吃东西")
    }
}
//存取属性描述符
Object.defineProperty(obj,"address",{
    //配置
    configurable:true,//属性是否是可配置的 默认 false 不能删除也不能更改 
    enumerable:true,//属性是否是可枚举的 默认 false 不能枚举 也不能遍历
	get:function(){
        return this._address
    },
    set:function(value){
        this._address = value
    }
})

console.log(obj.address) //chengdu

obj.address = "shanghai"

console.log(obj.address)//shanghai


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
  • 隐藏某一个私有属性不希望直接被外界使用和赋值
  • 如果希望截获某一个属性的访问和设置值的过程时,也会使用存取属性描述符

# 定义多个属性描述符

Object.defineProperties()

var obj = {
    _age : 18,
}
Object.defineProperties(obj,{
    name:{
    	value:"Okarin",
    	configurable:true, 
    	enumerable:true,
    	writable:true, 
    },
    age:{
        configurable:false, 
    	enumerable:false,
        get:function(){
            return this._age
        },
        set:function(value){
            this._age = value
        },
})
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20

# 获取属性描述符

  • Object.getOwnPropertyDescriptor()
var obj = {
    name:"okarin",
    age:18,
    eating:function(){
        console.log(this.name + "在吃东西")
    }
}
//数据属性描述符
Object.defineProperty(obj,"address",{
    //配置
    value:"chengdu",
    configurable:false
})

console.log(Object.getOwnPropertyDescriptor(obj,"age"))
//{ value: 18, writable: true, enumerable: true, configurable: true }
console.log(Object.getOwnPropertyDescriptor(obj,"address"))
// {
//     value: 'chengdu',
//     writable: false,
//     enumerable: false,
//     configurable: false
//   }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
  • Object.getOwnPropertyDescriptors()
var obj = {
    name:"okarin",
    age:18,
    eating:function(){
        console.log(this.name + "在吃东西")
    }
}
//数据属性描述符
Object.defineProperty(obj,"address",{
    //配置
    value:"chengdu",
    configurable:false
})

console.log(Object.getOwnPropertyDescriptors(obj))

// {
//     name: {
//       value: 'okarin',
//       writable: true,
//       enumerable: true,
//       configurable: true
//     },
//     age: { value: 18, writable: true, enumerable: true, configurable: true },
//     eating: {
//       value: [Function: eating],
//       writable: true,
//       enumerable: true,
//       configurable: true
//     },
//     address: {
//       value: 'chengdu',
//       writable: false,
//       enumerable: false,
//       configurable: false
//     }
//   }
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
33
34
35
36
37

# 对对象进行限制

# 禁止对象继续添加新的属性

Object.preventExtensions()

var obj = {
    name:"okarin",
    age:19
}

Object.preventExtensions(obj)

obj.address = "chengdu"

console.log(obj)

//{ name: 'okarin', age: 19 }
1
2
3
4
5
6
7
8
9
10
11
12

# 禁止对象配置/删除里面的属性

Object.seal()

var obj = {
    name:"okarin",
    age:19
}

Object.seal(obj)

delete obj.name

console.log(obj)

//{ name: 'okarin', age: 19 }
1
2
3
4
5
6
7
8
9
10
11
12

# 禁止对象修改属性

Object.freeze()

var obj = {
    name:"okarin",
    age:19
}

Object.freeze(obj)

obj.name = "Retr0"

console.log(obj)
1
2
3
4
5
6
7
8
9
10

# 对象的原型

JavaScript中每个对象都有一个特殊的内置属性[[prototype]],这个属性可以称之为对象的原型(隐式原型),这个特殊的对象可以指向另外一个对象

早期的ECMA没有规范如何查看[[prototype]],浏览器提供了一个__proto__属性便于查看。

var obj = { name: "okarin" }

console.log(obj.__proto__)//{}

1
2
3
4

ES5推出了Object.getPrototypeOf() 获取原型 方法可以查看[[prototype]]

var obj = { name: "okarin" }

console.log(Object.getPrototypeOf(obj))//{}
1
2
3

# 对象的原型的作用

当我们从一个对象中获取某一个属性是,它会触发[[get]]操作。

  1. 在当前对象中去查找对应的属性,如果找到就直接使用
  2. 如果在__proto__没有找到,就会沿着它的原型链去查找

# 函数的原型

函数作为对象来说,也是有[[prototype]]隐式原型

function foo(){

}

console.log(foo.__proto__)//{}

1
2
3
4
5
6

因为函数是一个函数,所以会多出来一个显示原型属性:prototype

function foo(){

}

console.log(foo.prototype)//foo {}

1
2
3
4
5
6

函数的显示原型 prototype 会指向函数默认的原型对象

这个对象包括 constructor 在内的很多属性,通过构造函数创建的对象,对象中的__proto__会指向函数的prototype

# 函数原型的属性

# constructor属性

function foo(){

}

console.log(Object.getOwnPropertyDescriptors(foo.prototype))

// {
//     constructor: {
//       value: [Function: foo],
//       writable: true,        
//       enumerable: false,
//       configurable: true
//     }
//   }
1
2
3
4
5
6
7
8
9
10
11
12
13
14

prototype.constructor 指向构造函数本身

# 给prototype添加属性

function foo(){

}

foo.prototype.name = "okarin"

var  f1 = new foo()
console.log(f1.name)//okarin
1
2
3
4
5
6
7
8
# 直接修改整个prototype对象
function foo(){

}

foo.prototype = {
    constructor:foo,
    name:"okarin"
}

var  f1 = new foo()
console.log(f1.name)//okarin

1
2
3
4
5
6
7
8
9
10
11
12
# 通过Object.defineProperty方式添加constructor
function foo(){

}

Object.defineProperty(foo.prototype,"constructor",{
    enumerable:false,
    configurable:true,
    writable:true,
    value:foo
})
1
2
3
4
5
6
7
8
9
10
Last Updated: 2021/12/19上午12:27:30