JavaScript笔记(面向对象编程)

时间:2023-02-11 20:36:49

面向对象编程

1 原型对象(prototype)

JavaScript的面向对象编程与大多数编程语言不一样,类与实例是大多数编程语言的基本概念,JavaScript不区分实例的概念,而是通过原型对象(prototype)来实现面向对象编程(原型对象就是有点的意思)。
JavaScript对每个创建的对象都会设置一个原型,指向它的原型对象。
当我们用obj.xxx访问一个对象的属性时,JavaScript引擎先在当前对象上查找该属性,如果没有找到,就到其原型对象上找,如果还没有找到,就一直上溯到Object.prototype对象,最后,如果还没有找到,就只能返回undefined。

2 创建对象

创建原型的方法除了简单地使用{...}创建之外,还可以使用其他别的方法。

2.1 继承原型对象

Object.create()方法可以传入一个原型对象,并创建一个基于该原型的新对象,但是新对象什么属性都没有

var Student = {
name:"匿名",
age:"21"
};

/* 以上面的对象作为原型对象创建一个新对象 */

function createstudent (name,age) {
var s=Object.create(Student);//基于Student原型创建一个新对象
s.name=name;//自定义属性
return s;//返回对象
}

var xiaoming=createstudent("小明");
xiaoming.name;//小明
xiaoming.age;//21

2.2 关键字new调用构造函数

JavaScript还可以用一种构造函数的方法来创建对象。它的用法是,先定义一个构造函数,再使用new关键字调用这个函数,并返回一个对象。一般的,如果构造函数没有return语句或者是return基本类型,则会自动忽视return并返回this,而当return的是一个对象时,则就返回这个对象。

var Student = function (name) {
this.name=name
};

var xiaoming=new Student("小明");
xiaoming.name;//小明

如果不写关键字new,这就是一个普通函数,在strict模式下,this指向undefined,会报错。
但是,如果写了关键字new,它就变成了一个构造函数,它绑定的this 指向新创建的对象,并默认返回this,也就是说,不需要在最后写return this;

为了区分普通函数和构造函数,按照约定,构造函数首字母应当大写,而普通函数首字母应当小写

1 那么此时的对象xiaoming的原型对象是谁呢?

构造函数有一个prototype属性(注意只有构造函数才拥有),这个prototype属性指向的对象就是通过其创建的对象的原型对象了,也就是说上述的对象xiaoming其原型对象就是Student.prototype,而这些子对象其实可以通过__proto__这个非标准用法来查看。

Student.prototype===xiaoming.__proto__;//true

Object.getPrototypeOf(xiaoming) === Student.prototype; // true

// 验证继承关系:
xiaoming instanceof Student; // true

还有使用构建函数创建的对象都会继承原型对象constructor属性,它指向函数本身。

xiaoming.constructor===Student.prototype.constructor;//true

Student.prototype.constructor === Student; // true,等同于构建函数

此外,使用new关键字创建多个对象时,若这些对象的有信息是固定的(比如说共享同一个函数),那么就无需填写在构建函数里头,可以直接绑定在原型对象,这样运行起来可以省很多内存,如:

Student.prototype.hello=function () {
alert('Hello, ' + this.name + '!');
};
xiaoming.hello();

3 原型继承

假如现在有一个Person对象,想要在Person对象下创建一个Student对象,使其有继承关系,实现继承的方法以下方式:

  • 第一种,使用new,创建Student
Student.prototype = new Person();
Student.prototype.constructor = Student;//修复构造函数

使用new有个弊端,就是假如Person的构造函数带有参数的话,就会变得局限了。

  • 第二种,使用Object.create(),创建Student
Student.prototype = Object.create(Person.prototype);
Student.prototype.constructor = Student;//修复构造函数

要制作原型继承,第一步当然就是定义新的构造函数用来继承原型对象了。

  • 定义新的构造函数,并在内部用call()调用要“继承”的构造函数,并绑定this;

第二步,创建一个原型对象的空函数(应该空实例也行),并被新的构造的原型对象继承

  • 借助中间函数F实现原型链继承,最好通过封装的inherits函数完成,inherits函数的主要功能是:被新的构造的原型对象继承,以及修复其的构造函数

Obeject.create()是ES5从引进的功能,那么在低版本如何创建继承呢?其实也很简单,只需创建一个空函数继承父原型对象,在调用new

/* 旧版本创建Object.create() */

//假如Object对象没有create属性
if (!Object.create){
Obeject.create = function(proto) {
function F () {}
F.prototype = prtoto;//把参数(父原型对象)赋值
return new F;//返回以及调用new的对象
};
}

4 class继承

上面的太难太麻烦了?救星来了。

新的关键字class从ES6开始正式被引入到JavaScript中。class的目的就是让定义类更简单,注意是定义类,不仅仅用于继承

class的定义包含了构造函数constructor定义在原型对象上的属性或函数(注意没有function关键字),如下使用class关键字创建构造函数:

/*
class的定义包含了构造函数constructor和定义在原型对象上的函数hello()(注意没有function关键字)
*/
class Student {
constructor(name) {
this.name=name;
}
hello () {
alert('Hello, ' + this.name + '!');
}//没有function关键字定义
}

var xiaoming=new Student("小明");

4.1 class继承

用class定义对象的另一个巨大的好处是继承更方便了,直接通过extends来实现,还需要使用super调用父类的构造方法:

class PrimaryStudent extends Student {
constructor(name, grade) {
super(name); // 记得用super调用父类的构造方法!
this.grade = grade;
}

myGrade() {
alert('I am at grade ' + this.grade);
}
}