JavaScript面向对象

461 阅读3分钟

创建对象

JavaScript对每个创建的对象都会设置一个原型,指向它的原型对象。
当我们用obj.xxx访问一个对象属性的时候,JavaScript引擎会现在当前对象上查找该属性,如果找不到,就在其原型对象上找,如果还是没有,就会上溯Object.prototype对象找,如果还没有的话就会返回 undefined

  • 创建一个 Array对象:
    let arr = [1,2,3,4,5];
    它的原型链是:
    arr ----> Array.prototype ----> Object.prototype ----> null
    Array.prototype定义了indexOf(),shift()等方法,所以可以在Array的对象上直接调用这些方法。
  • 创建一个函数
function f1() {
return 0;
}

f1()的原型链:
f1 ---> Function.prototype ----> Object.prototype ----> null
由于Function.protype定义了call(),apply()等方法,因此所有函数都可以调用这些方法。如果原型链很长,访问一个对象的属性就会花更多的时间来查找而变得更慢。

构造函数

用构造函数的方法创建一个对象,首先定义一个构造函数:

function Person(name,age) {
	this.name = name;
    this.age = age;
    this.sayName = function() {
    	console.log('Hi,'+this.name);
        }
        }

接下来用关键字 new来调用函数并返回一个对象。

let Nick = new Person('Nick',23);
Nick.sayName();//'Hi,Nick'

写了 new关键字,它变成一个构造函数,它绑定的 this指向新创建的对象,并默认返回 this,也就是说不需要在最后写return this;
Nick的原型链如下:
Nick ---> Person.prototype ---> Object.prototype ---> null
也就是 Nick的原型指向 Person原型,如果再创建别的tom,jerry....这些对象的原型都是和 Nick一样的。
此外,通过new Person()创建的对象还从原型上获得了一个 constructor属性,它指向函数 Person本身。

Nick.construcotot === Person.prototype.constructor;
Person.prototype.construcor === Person;
Object.getPrototypeOf(Nick) === Person.prototype;

有一点需要注意,创建两个新的对象 TomJerry,但是他们俩的 sayName()函数虽然名称和代码相同,但实际上是两个不同的函数,如果通过 new Person()创建很多个对象,他们只需要共享一个sayName()函数就可以了,能节省很多内存。修改代码如下:

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

Person.prototype.sayName = function() {
	console.log('Hi,'+this.name);
    }

特别注意

  • 对象的.__proto__ === 其构造函数.prototype
  • Object.prototype是所有对象的直接或间接原型
  • 所有函数都是由 Function构造的,任意函数.__proto__ === Function.prototype

class继承

ES6引入了 class关键词, class的引入目的是让定义类更简单。
如果使用 class关键字来编写 Person

class Person {
	constructor(name) {
    this.name = name;}
    
sayName() {
	console.log('Hello,' + this.name);
    }
    }

通过比较可以发现使用 class包含了构造函数 constructor和定义在原型对象上的函数 sayName()(没使用 function关键字),这样就避免了 Person.prototype.sayName = fucntion(){...}这样的代码。


使用 class一个巨大的好处就是更方便继承。如果要从 Person派生一个 otherPerson,原型继承的中间对象、原型对象的构造函数都不需要考虑了,可以直接通过 extend来实现。

class otherPerson extends Person{
	constructor(name,city) {
    super(name); //使用supper调用父类的构造方法
    this.city = city;
    }
    myCity() {
    aler (' I am from ' + this.city);
    }
   }

otherPerson的定义也是 class关键字来实现的,而 extends表明了原型链对象来自 Person, otherPerson需要 name city两个参数,并且通过 super(name)来调用父类的构造函数。并且otherPerson已经自动获得了父类Person的sayName()方法。
class与原有的原型继承没有任何区别,只是简化了原型代码。