我一直觉得javascript的面向对象是一个很不好理解、很抽象的概念,我斗胆在这里整理下我的理解吧,首先从什么是对象开始。
对象 Object
通常来说,对象可以理解成一个容器,这个容器里装着一系列以键值对(key/value)形式出现的属性的合集,并且这些属性可以通过key进行索引。对象具有以下特点:
- 唯一标识性,即使两个对象的内容一样,但是也不是同一个对象。
- 具有状态,即数值。
- 具有行为,也就是调用的方法和函数。
基于js对象以上的特点,对象具有高度的动态性,以至于使用者可以在运行时改变状态和行为,状态和行为就可以统称为属性。
Property Descriptor
对象的每一个属性还可以用property descriptor去描述内部的行为方式,可以用Object.getOwnPropertyDescriptor()来获取已经定义的descriptor;相应地,也可以用Object.defineProperty()去定义descriptor,前提是configurable没有被关闭,即不是false。
通常情况下property descriptor是定义一个value属性,但是descriptor也有accessor属性(访问器属性),即getter/setter。getter的值通常是一个函数或者是undefined,在取属性值时被调用;相应的,setter也是函数或者是undefined,在设置属性值时被调用。他们可以允许使用者在读写属性时得到完全不一样的值,也被认为是一种函数的语法糖。
除此之外property descriptor还有writable(属性是否能被赋值)、enumerable(属性是否能在for in loop中被枚举)、configurable(属性是否能在被删除或者改变特征值)。
深拷贝/浅拷贝
如果使用赋值操作符=复制一个对象,其实复制的只是一个引用对象,其引用变量存储的是所引用的对象的地址,也就是说如果把一个引用对象a赋值给引用对象b,其实他们指向的都是内存中的同一个对象,如果a.property的值改变的话,那么b.property的值也会改变,反之亦然。
浅拷贝就是只复制了对象第一层的属性值,如果第一层的属性值是一个对象,那么浅拷贝只会复制一个指向对象的引用指针,而非对象本身,如果指向的对象属性值有更改的话,那么所拷贝的对象中的属性值也会随之更改。深拷贝是会把对象的第一层级的属性值,以及所有嵌套的属性值全都复制过来,存储在一个新的地址中,无论被复制的对象有任何修改都与新复制的对象无关。
浅拷贝的方法是spread操作符...或者是Object.assign()。e.g. {...obj}, Object.assign({}, obj)。深拷贝的方法就是JSON.parse()和JSON.stringify(),e.g. JSON.parse(JSON.stringify(obj))
分类
js可以把对象分为以下几类:
- 宿主对象 host Objects:由js宿主环境提供的对象
- 内置对象 Built-in Objects:由js语言提供的对象
- 固有对象 Intrinsic Objects:由标准规定,在js运行中自动创建的对象实例
- 原生对象 Native Objects:由用户通过Array,RegExp等内置构造器创建的对象
- 普通对象 Ordinary Objects:由语法、对象构造器、class关键字创建的对象
创建对象
最简单的方法就是使用对象字面量通过new Object()创建,但是如果批量生产的话就会很冗余、重复。那么就可以使用工厂函数创建对象,传入要创建的对象的参数,然后return出来一个对象,当要创建对象的时候就调用这个函数。但是即使是这样,却无法知道一个对象的类型是什么,所以就有了构造函数,构造函数就是自定义的普通对象的类型。
function fruit(name, color){
return {
name: name,
color: color,
isFresh: function(){
console.log("The" + this.name + "is fresh!");
}
}
}
var apple = fruit('apple', 'red');
构造函数 Class
构造函数是这样的:
function Fruit(name, color){
this.name = name;
this.color = color;
this.isFresh = function(){
console.log("The" + this.name + "is fresh!")
}
}
var apple = new Fruit('apple', 'red');
apple.isFresh(); // => The apple is fresh!
构造函数和工厂函数的区别是:
- 没有显式地创建一个对象,而是在创建实例的时候使用
new关键字创建; - 直接把属性和方法赋值给了
this对象; - 没有
return语句; - 函数名首字母大写。
由此可见使用构造函数创建一个对象需要使用new关键字,实际上经历了以下四个步骤:
- 创建一个新的对象;
- 把构造函数的作用域指向新的对象,使用
this; - 执行构造函数里的代码;
- 返回一个新的对象。
ES6 class
而ES6引入了关键字class,以至于就不需要使用function + new的组合创建构造函数,示例如下:
class Fruit(){
constructor(name, color){
this.name = name;
this.color = color;
}//数据型属性最好都写在构造器里
//method
generateSentence(){
return "The" + this.name + "is fresh!";
}
//getter
get fresh(){
return this.generateSentence();
}
}
ES6还支持extends关键字,继承父类的属性,在继承父类属性的同时也可以覆盖父类的方法,当使用同样名称的方法的时候再声明一遍,但是在constructor中如果想申明一些新的数据型属性值,但是有一些父类已经有了的不想再声明一遍的就可以使用super()方法把参数传给父类构造器。例子如下:
class Fruit{
constructor(name, color){
this.name = name;
this.color = color;
}//数据型属性最好都写在构造器里
//method
printName(){
console.log(this.name);
}
}
class Grape extends Fruit{
constructor(name, color, specie){
super(name, color);
this.specie = specie;
}
//method
printName(){
console.log('The' + this.name + ‘belongs to’ + this.specie + '.');
}
}
var a = new Grape('Chardonnay');
a.printName(); // => The grape belongs to Chardonnay.
原型链
当很多对象都用一些共同的属性的时候,为了避免大量的重复,可以把这些属性和方法都放在原型prototype上。例如所有的葡萄都是圆的,那么Grape.prototype.shape = 'round';。在js中,每一个构造函数都一个prototype属性,指向另一个对象,称为构造函数的prototype对象,这个对象的属性和方法都会被构造构造函数所拥有。我们把所有对象实例需要共享的属性和方法都可以写在prototype对象上。与此同时,每一个对象实例都有一个__proto__指针指向prototype对象,所以实例对象.__proto__ = 构造函数.prototype。
当解析器读取一个对象的属性时,会先访问对象本身,如果对象本身没有就寻找对象的原型,如果还没有就寻找对象原型的原型,直至原型为null为止,从对象本身到null这一段线路就形成了原型链。同时js还提供一个一些内置函数操纵原型,例如Object.create,Object.getPrototpeOf,Object.setPrototypeOf。
面向对象
面向对象编程--Object Oriented Programming,简称 OOP ,是一种编程开发思想。在面向对象中,对象是程序的最小单元,对象把数据和程序封装起来提供对外访问的能力,提高了代码的可复用性、灵活性和扩展性。面向对象的程序范式有以下几个特点:
- 抽象性:隐藏了很多细节,把数据和程序都封装在对象中,只向使用者展示必要的细节,使用者只需要调用相应的接口即可,不需要搞明白所有的细节。
- 封装性:封装使属性和方法都在成为类的私有属性,从而外部无法访问。由于封装隐藏了具体的实现信息,所以内部进行一些升级或者改造,对于调用接口的使用者来说影响并不大,以至于提高了代码的可维护性。
- 继承性:继承性是代码复用的基础,在以上关于类的讲解中也具体解释了子类如何继承父类,包括子类如何覆盖父类的方法。
- 多态性:指的是子类型是一种多态的形式,不同类型的对象实体有统一接口,相同的消息给予不同的对象会引发不同的动作。
Reference: github.com/getify/You-… github.com/getify/You-… github.com/getify/You-… www.freecodecamp.org/news/copyin… www.geeksforgeeks.org/what-is-sha… medium.com/version-1/c… time.geekbang.org/column/arti… time.geekbang.org/column/arti… time.geekbang.org/column/arti… developer.mozilla.org/en-US/docs/… www.freecodecamp.org/news/object… juejin.cn/post/684490… www.zhihu.com/question/31…