JavaScript进阶讲解九—>面向对象三(原型链)

107 阅读2分钟

一、面向对象的特性

  1. 封装:将属性和方法封装到一个类中,就是封装的过程。
  2. 继承:继承是面向对象中非常重要的,不仅仅可以减少重复代码的数量,也是多态前提(纯面向对象中)
  3. 多态:不同的对象在执行时表现出不同的形态。

二、原型链

var obj = {
    name: 'xt'
}

obj.__proto__ = {}
obj.__proto__.__proto__ = {}
obj.__proto__.__proto__.__proto__ = {
    age: 18
}

console.log(obj.age); // 18

上面的代码,在obj中是没有age属性的,但是当我们在其__proto__上添加了age后他是可以找到的,说明他的查找,是一层一层的在查找。如下图: 在这里插入图片描述 我们知道了其查找规则,但是他会不会一直找了,答案是不会,因为他是有顶层的,为什么我们能确定他是有顶层的呢?

var obj = {
    name: 'xt'
}

console.log(obj.age); // undefined

因为当obj 中没age属性时,我们直接打印obj.age,他是能马上给我们返回undefined的,那他的顶层又是什么呢?

var obj = {
    name: 'xt'
}

console.log(obj.__proto__); // [Object: null prototype] {}

[Object: null prototype] {} 这个就是我们的顶层,这个是在node环境下的打印,看着不太明显,我们可以在浏览器中执行(浏览器中他为了我们方便调试,是有帮我们特殊处理的,所以我们会更容易看懂) 在这里插入图片描述 看这里的__proto__是null,其实我们对象原型的顶层,就是null

我们刚看了对象的,那我们在来看看函数的

function foo(params) {
        
    }

console.log(foo.prototype.__proto__.__proto__);// null

我们可以看到这里依旧是null,同时我们也应该知道函数的prototype属性值中,也是有__proto__的。当然,我们还可以知道,这里的foo其实是继承Object的,所以原型链最顶层的原型对象就是Object的原型对象

三、继承

继承的主要目的,就是为了减少重复代码。 当我们学完了,原型链其实我们就可以通过原型链来自己实现继承。

// 父类
function Person() {
    this.name = 'xt'
}

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

// 子类
function Student() {
    this.sno = 1
}

// 通过原型继承
Student.prototype = new Person()

Student.prototype.studying = function() {
    console.log(this.name + 'studying');
}

var stu = new Student()
console.log(stu.name); // xt

通过这种方法实现继承的弊端

  1. 当我们打印stu的时候(node中),我们是看不到name这个属性的,因为name在person中。
  2. 如果是引用类型,可能会有传值引用问题。
  3. 不能给Person传递参数

如何解决

借用构造函数继承

function Person(name) {
    this.name = 'xt'
}
...
Student.prototype.studying = function() {
	Person.call(this, name)
    console.log(this.name + 'studying');
}
...

虽然,我们前面的弊端解决了,但是,又有其他弊端,比如:

  1. Person至少要被调两次
  2. stu原型对象会多出一些属性,但是这些属性时没有存在的必要的

如何解决

寄生组合继承

// 父类
function Person(name) {
    this.name = name
}

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

// 子类
function Student(name, sno) {
    Person.call(this, name)
    this.sno = sno
}

// 寄生式核心代码
function inheritPrototype(subType, superType) {
    subType.prototype = Object.create(superType.prototype)
    subType.prototype.constructor = subType
}

inheritPrototype(Student, Person)

Student.prototype.studying = function() {
    console.log(this.name + 'studying');
}


var stu = new Student('xt', 1)
console.log(stu); // Student { name: 'xt', sno: 1 }