重温OOP之原型与继承

      JS 原型链与继承是 JS 中的重点,这篇文章将针对原型链和继承通过代码的方式略作总结。

原型链

      首先,我们通过 new 操作符创建对象的过程来说说原型链。

1
2
3
4
5
6
function Person(name) {
this.name = name;
return name;
}
let classes = new Person("Lucy");
console.log(classes); // person {name: "Lucy"}

      我们用代码的形式来解析下以上用 new 操作符创建实例的过程。

1.var classes = {};    //创建一个空对象

2.classes.prop = Person.prototype    //将空对象的proto成员指向 Person 函数对象的 prototype 成员对象

3.Person.call(classes);     //将 Person 函数的指针指向 classes
注意我们需要牢记的是只有对象才有proto属性,只有函数才有 prototype 属性。由于在 js 中函数也是对象,所以函数也有prop属性。


原型链

      从这张图我们可以看到proto属性都是从一个对象指向他们的原型对象。当我们在访问一个对象的属性时,如果这个对象内部不存在这个属性,那么它就会它的proto属性指向的那个父对象当中去找,如果父对象也不存在该属性,则会去再上一层父对象的proto属性指向的对象中找。如果还是没有找到,则会一层一层向上查找,一直到找到 null 为止,如果还没有找到,则返回 Undefined。像这种通过proto属性来连接对象直到 null 为止的一条链就是原型链。

注意:使用 new 来创建对象,调用构造函数的时候,如果 return 的是对象,则会返回该对象;如果 return 的是非对象,则会忽略返回值

1
2
3
4
5
function person() {
return 1;
}
let classes = new person();
console.log(classes); //person {}

Object.prototype.toString.call(obj)

      说完了原型链,我们来实际应用下把。Object.prototype.toString.call(obj)这个方法是检测对象类型最通用的办法。通过原型链的知识,我们看看为什么这个方法能检测数据类型,而obj.toString()只能将对应类型转成字符类型。

1
2
3
4
5
6
7
8
9
console.log(Object.prototype.toString.call("Lucy")); //[object String]
console.log(Object.prototype.toString.call([1, 2])); //[object Array]
console.log(Object.prototype.toString.call(true)); //[object Boolean]
console.log(Object.prototype.toString.call(new Date().getTime())); //[object Number]

console.log("Lucy".toString()); //Lucy
console.log([1, 2].toString()); //1,2
console.log(true.toString()); //true
console.log(new Date().getTime().toString()); //1555840282073

      从代码可以推测出 toString 是 Object 的原型方法,而 Array,Boolean,Date 等是 Object 的实例,各自上都有 toString 方法。根据以上说的原型链,因为各自上已经有 toString 方法,所以调用的是各自的 toString 方法,而不会去调用 Object 原型上的 toString 方法,所以 obj.toString 只能将 obj 转成字符串类型。要想检验对象的类型,应该去调用 Object 原型上的 toSting 方法。接下来,我们将 String 上的 toString 方法删掉,来验证我们的猜测。

1
2
3
4
5
6
var string = "Lucy";
console.log(String.prototype.hasOwnProperty("toString")); //true
console.log(string.toString()); //Lucy
delete String.prototype.toString;
console.log(String.prototype.hasOwnProperty("toString")); //false
console.log(string.toString()); //"[object String]"

      我们在删掉了 String 上的 toString()后,再用 string.toString()方法调用时,因为 string 上没有 toSting()方法了,它就会沿着原型链,调用 Object 上的 toString 方法,所以才能返回与 Object.prototype.toString.call(obj)相同的结果。就验证了我们的猜测是对的。

继承

      接下来我们利用原型实现下继承。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
function Person(name, age) {
this.name = name;
this.age = age;
}
Person.prototype.hello = function() {
return `你好,我叫${this.name},我${this.age}岁,很高兴认识你`;
};
Person.prototype.school = "UV";

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

//Object.create():创建一个空对象,并且让这个对象的原型指向create()中的参数
Student.prototype = Object.create(Person.prototype);
Student.prototype.constructor = Student;
Student.prototype.classRoom = function() {
return `我是${this.className}班的`;
};
var demo = new Student("Lucy", 20, 11);
console.log(demo.hello()); //你好,我叫Lucy,我20岁,很高兴认识你
console.log(demo.school); // UV
console.log(demo.classRoom()); // 我是11班的

      以上代码中,我先定义了一个 Person 构造函数,并且在其原型上添加了一个 hello 方法和 school 属性。然后我又定义了一个 Student 函数并在原型上定义了一个 classRoom 方法。将 Student.prototype 的proto指向了 Person.prototype。完成了一个基础的继承例子。
注意:
1.不能用 Student.prototype = Person.prototype
这样在增加 student.prototype 的同时也会增加 Person,维护性很差
要指定 Student.prototype.constructor,如果没有指定的话,Student.prototype.constructor 会指向 Person
标准的继承写法

1
2
Student.prototype = Object.create(Person.prototype);
Student.prototype.constructor = Student;

接下来,我们看看上面这个代码的原型链吧


继承

Copyright ©2019 guowj All Rights Reserved.

访客数 : | 访问量 :