面向对象概念
对象是JavaScript中一个非常重要的概念,这是因为对象可以将多个相关联的数据封装到一起,更好的描述一个事物:
比如我们可以描述一辆车:Car,具有颜色(color)、速度(speed)、品牌(brand)、价格(price),行驶(travel)等等;
比如我们可以描述一个人:Person,具有姓名(name)、年龄(age)、身高(height),吃东西(eat)、跑步(run)等等;
用对象来描述事物,更有利于我们将现实的事物,抽离成代码中某个数据结构:所以有一些编程语言就是纯面向对象的编程语言,比Java;你在实现任何现实抽象时都需要先创建一个类,根据类再去创建对象;
JavaScript的面向对象
JavaScript其实支持多种编程范式的,包括函数式编程和面向对象编程:
JavaScript中的对象被设计成一组属性的无序集合,像是一个哈希表,有key和value组成; key是一个标识符名称,value可以是任意类型,也可以是其他对象或者函数类型; 如果值是一个函数,那么我们可以称之为是对象的方法;
创建JS对象的方法
单个对象创建的方法
// 方法一:
var obj = {
name: 'foo',
age: 18,
};
// 方法二:
var obj = new Object();
obj.name = 'foo';
obj.age = 18;
多个对象创建的方法
工厂模式
可以通过上述方式创建,但如果需要创建相同属性的多个对象,可以使用工厂模式;
工厂模式其实是一种常见的设计模式,通常我们会有一个工厂方法,通过该工厂方法我们可以产生想要的对象;
// 工厂模式:
function createPerson(name, age) {
var obj = new Object();
obj.name = name;
obj.age = age;
return obj;
}
var person1 = createPerson('foo', 18);
var person2 = createPerson('bar', 20);
构造函数
构造函数也称之为构造器(constructor),通常是我们在创建对象时会调用的函数;在其他面向的编程语言里面,构造函数是存在于类中的一个方法,称之为构造方法;但是JavaScript中的构造函数有点不太一样;
function foo() {
console.log("foo~, 函数体代码")
}
// foo就是一个普通的函数
foo() // foo~, 函数体代码
// 换一种方式来调用foo函数: 通过new关键字去调用一个函数, 那么这个函数就是一个构造函数了
var f1 = new foo // foo~, 函数体代码
console.log(f1) // [Function: foo]
如果一个函数被使用new操作符调用了,那么它会执行如下操作:
- 在内存中创建一个新的对象(空对象);
- 这个对象内部的[[prototype]]属性会被赋值为该构造函数的prototype属性;;
- 构造函数内部的this,会指向创建出来的新对象;
- 执行函数的内部代码(函数体代码);
- 如果构造函数没有返回非空对象,则返回创建出来的新对象;
// 规范: 构造函数的首字母一般是大写
function Person(name, age, height, address) {
this.name = name
this.age = age
this.height = height
this.address = address
this.eating = function() {
console.log(this.name + "在吃东西~")
}
this.running = function() {
console.log(this.name + "在跑步")
}
}
var p1 = new Person("张三", 18, 1.88, "广州市") // 创建一个新的对象
var p2 = new Person("李四", 20, 1.98, "北京市")
console.log(p1) // Person {name: "张三", age: 18, height: 1.88, address: "广州市"}
console.log(p2)
p1.eating() // 张三在吃东西~
p2.eating()
操作JS对象的方法
普通的属性操作方式:
// 设置属性:
obj.name = 'bar';
// 获取属性:
console.log(obj.name);
// 删除属性:
delete obj.name;
数据属性描述符
属性操作的控制:通过属性描述符可以精准的添加或修改对象的属性,属性描述符需要使用 Object.defineProperty 来对属性进行添加或者修改:
Object.defineProperty(obj, 'name', {
value: 'bar',
writable: true, // 可写
enumerable: true, // 可枚举, 可以通过for in循环获取到, 如果设置为false, 则不能通过for in循环获取到, 也不能console.log(obj)获取到
configurable: true , // 可配置,可以删除属性
});
构造函数缺点:每次创建一个新的对象,都需要重新创建一个函数对象实例,会占用大量的内存;
存取属性描述符
存取属性描述符:1.隐藏某一个私有属性被希望直接被外界使用和赋值 2.如果我们希望截获某一个属性它访问和设置值的过程时, 也会使用存储属性描述符
obj = {
_name: 'foo',
}
function foo(value){
console.log('get value', value);
}
function bar(value){
console.log('set value', value);
}
Object.defineProperty(obj, 'name', {
enumerable: true,
configurable: true,
// value: 'foo', //使用存取属性描述符的话,只能使用getter和setter来控制属性的访问和赋值
// writable: true, // 同上
get: function() {
foo(this._name);
return this._name;
},
set: function(value) {
bar(value);
this._name = value;
}
});
同时定义多个属性描述符
Object.defineProperties() 方法直接在一个对象上定义多个新的属性或修改现有属性,并且返回该对象。
Object.defineProperties(obj, {
name: {
value: 'foo',
writable: true,
enumerable: true,
configurable: true,
},
age: {
value: 18,
writable: true,
enumerable: true,
configurable: true,
}
});
获取某一个属性的属性描述符
获取对象的属性描述符:getOwnPropertyDescriptor, getOwnPropertyDescriptors
var obj = {
// 私有属性(js里面是没有严格意义的私有属性)
_age: 18,
_eating: function() {}
}
Object.defineProperties(obj, {
name: {
configurable: true,
enumerable: true,
writable: true,
value: "foo"
},
age: {
configurable: true,
enumerable: true,
get: function() {
return this._age
},
set: function(value) {
this._age = value
}
}
})
// 获取某一个特性属性的属性描述符
console.log(Object.getOwnPropertyDescriptor(obj, "name"))
console.log(Object.getOwnPropertyDescriptor(obj, "age"))
// 获取对象的所有属性描述符
console.log(Object.getOwnPropertyDescriptors(obj))
Object的方法对对象限制
禁止对象扩展新属性:preventExtensions
给一个对象添加新的属性会失败(在严格模式下会报错);
密封对象,不允许配置和删除属性:seal
实际是调用preventExtensions p并且将现有属性的configurable:false
冻结对象,不允许修改现有属性: freeze
实际上是调用seal p并且将现有属性的writable: false
Object.freeze() // 冻结对象,不能再修改属性值,不能再删除属性,不能再添加属性,不能再修改属性的可枚举性,不能再修改属性的可配置性,不能再修改属性的可写性
Object.seal() // 将对象封闭,不能再修改属性值,不能再删除属性,不能再添加属性,可以修改属性的可枚举性,可以修改属性的可配置性,可以修改属性的可写性
Object.preventExtensions() // 阻止对象增加新的属性,不能再修改属性值,不能再删除属性,可以修改属性的可枚举性,可以修改属性的可配置性,可以修改属性的可写性
Object.isFrozen() // 判断对象是否被冻结