Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

原形链和继承——《你不知道的JavaScript》读书笔记 #378

Open
HuaYJ1996 opened this issue Dec 30, 2019 · 0 comments
Open

Comments

@HuaYJ1996
Copy link

类和继承

在面向对象对语言中,最核心的三个特性就是:继承,封装,多态。而在这三个特性的实现过程中,类的概念尤为突出。类是面向对象语言的程序设计中的概念,是面向对象编程的基础。它本质上是一种引用数据类型,是一种复杂的数据结构,而并不是数据。
用一个简单的例子描述类和对象的关系就是:狗是一个类,而旺财是一个对象。对象是类的一个实例,在java等语言中我们使用new关键字去实例化对象。
而类与类的关系中,继承是一种最常见的关系。比如,动物类和狗类就是一种继承关系,狗类继承类动物类,如果再往下,狗类又可以派生出萨摩耶类、二哈类等。在java中,可以使用extends关键字去实现类的继承。
在ES6中,我们经常看到使用如class A extends B{ ...}的语句,那么是否意味着Javascript也有类的概念、也是一门面向对象的语言呢?

JavaScript中的“类“

在ES6语法中,我们常能看见这样的语句:

    class People{
        constructor(name,age){
            this.name = name;
            this.age = age
        }
        say(){
            console.log("hello");
        }
    }

    class Teacher extends Perple{
        constructor(name,age,id){
            super();
            this.teacherId = id;
        }
        teach(){
            console.log("teach English");
        }
    }

在上面的语句中,对面向对象语言稍有了解的人都会觉得很亲切,让我们误以为js和java一样是基于类继承的。然而,这只是ES6的一种欺骗效果。这样的语句能让我们很方便地使用继承,但是本质上,js中并没有类的概念。以上的语句在实际执行中会被翻译成以下代码:

    function People(name,age){
        this.name = name;
        this.age = age; 
    }
    People.prototype.say=function(){
        console.log("hello)
    }
    function Teacher(name,age,id){
        People.call( this, name );
        this.teacherId = id;
    }

在java中使用new作为关键字实例化一个对象,而在js中,同样有new关键字,那它又做了什么呢?
通常我们会在new后面接上一个构造函数,这让我们陷入另一个误区。实际上,new后面也可以放一个普通的函数。

    function foo(){
        console.log("hello");
    }
    let fn = new foo(); // "hello"
    console.log(fn); // foo {}

在使用new关键字之后,会构建一个对象。当new后面的函数不是构造函数时,let fn = new foo()可以被理解为两部分。首先,它执行了foo函数,因此会打印出“hello”;其次,由于new关键字,new foo()会生成一个对象,但由于foo并不是一个构造函数,这个生成的对象仅仅是一个空对象。
由此我们可知,构造函数的首字母大写只是一种习惯,事实上符合语法规范的命名均可(但是不按规矩写会被疯狂吐槽)。

JavaScript中的继承

js使用了各种方式去模拟类的概念,同样,它也使用各种方式让“继承”看起来更像是“继承”。事实上,js是相比于其他语言更深入贯彻“面向对象”概念的语言,因为它甚至没有类的概念,有的只是原形链上的一个又一个对象。
在js的继承中,我们如果将继承的对象打印出来,就会看到__proto__属性,通过这个属性一级一级向上,我们就能“看”到对象的原形链。
在People和Teacher的例子中,如果我们new一个Teacher对象,那么程序将会创造一个新的对象,这个新的对象的__proto__将会指向Tearch对象(或者说这个伪装的类)。如let teacher = new Teacher('Lee',22,'001');,之后调用teacher.say()时,程序会首先寻找teacher对象本身是否有say方法,没有则去它的上一级Teacher对象那里去寻找,还是没有找到,再去Teacher对象的上一级People对象那里去寻找,在此找到了这个方法并调用。
在js中,寻找变量分为左查询和右查询,个人理解是:右查询是找到变量的值,用于读取数据;左查询需要找变量的地址,用于写入操作。对于对象而言,对于右查询,如果在该对象中没有找到这个属性或方法,则会沿着原形链往上寻找,直到找到或者到达原形链尽头;而对于左查询,则不会沿原形链往上寻找,如果在该对象中没有找到,则给它创建一个新的属性。代码示例如下:

    class People{
        constructor(){
            this.name = 'Tom';
        }
        say(){};
    }
    class Teacher extends People{
        constructor(name){
            super();
            // this.name = name;
        }
    }

    let teacher = new Teacher();
    console.log(teacher.hasOwnProperty('say')); // false
    teacher.say = function(){console.log('hello')};
    console.log(teacher.hasOwnProperty('say')); // true
    delete teacher.say;
    console.log(teacher.hasOwnProperty('say')); // false

总结

js中并不是使用真实的类的概念去实现继承,但它确实是一门面向对象的语言。js中继承的实现是神奇的,个人的理解还不够透彻,之后还需要继续更深入地理解。

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant