面向对象JS ES5/ES6 类的书写 继承的实现 new关键字执行 this指向 原型链

Posted 飞叶飞花

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了面向对象JS ES5/ES6 类的书写 继承的实现 new关键字执行 this指向 原型链相关的知识,希望对你有一定的参考价值。

一 、什么是面向对象?

  面向对象(Object Oriented),简写OO,是一种软件开发方法。
  面向对象是一种对现实世界理解和抽象的方法,是计算机编程技术发展到一定阶段后的产物。
  面向对象的概念和应用已超越了程序设计和软件开发,扩展到如数据库系统、交互式界面、应用结构、应用平台、分布式系统、网络管理结构、CAD技术、人工智能等领域。
  面向对象是相对于面向过程来讲的,面向对象方法,把相关的数据和方法组织为一个整体来看待,从更高的层次来进行系统建模,更贴近事物的自然运行模式。

编程范式

  一般可以分为两种:

    声明式,告诉计算机应该做什么,但不指定具体要怎么做。如:html,css等

    命令式,关注计算机执行的步骤,即一步一步告诉计算机先做什么再做什么。如:js,c++,java,c#,python等

 

  其中命令式编程又可以为两类

    面向过程:

    将我们要解决问题所需要的步骤分析出来,然后用函数把这些步骤一步一步实现(有序步骤),关注细节。

    面向对象:

    将问题中的对象抽象出来,既把构成问题的事物分解成各个对象,而解决问题的不是一个个过程化的步骤,

    而是利用对象描述、处理“问题”的行为集合(离不开面向过程)。

 

二、面向对象的基本概念

1、对象

  对象的含义是指具体的某一个事物,即在现实生活中能够看得见摸得着的事物,有句话叫做:万物皆对象。

  对象是类的具体实例,对象的抽象是类。

2、类

  类是具有相同特性(数据元素)和行为(功能)的对象的抽象,代表一类事物的抽象描述。

  在代码中:类好比是一个模板,可以批量生产。

  类是对象的抽象,类的具体化就是对象。


  类的实现

 

  原本JS不支持面向对象(oo),所以在ES5用函数模拟出类,在ES6时加入语法糖,书写起来更加清晰、更像面向对象编程的语法。

  类的本质是函数:构造函数+全局公共区域 prototype。

ES5

//混合模式:构造函数模式+原型模式
function Person(name, age) {//构造函数模式
    this.name = name;  /*属性*/
    this.age = age;
    this.study = function () {
        console.log(this.name + ‘在运动‘);
    }
}

//原型模式 prototype,原型链上面的属性会被实例对象共享
Person.prototype.sex = "男";//共有的属性
Person.prototype.exercise = function () {//共有的函数
    console.log(this.name + ‘在锻炼‘);
}

//类的静态属性和方法不会被实例对象共享
Person.num = 11234;//定义静态属性
Person.printNum = function () {//定义静态方法
    console.log(this.num);
}//注:函数也是一个对象,所谓的静态属性方法,本质就是给名为Person的对象添加了属性和方法

var stu = new Person(‘张三‘, 20);
console.log(stu);//Person { name: ‘张三‘, age: 20, study: [Function] }
console.log(stu.sex);//
stu.study();//调用自带方法 => 张三在运动
stu.exercise(); //调用原型方法 => 张三在锻炼

console.log(Person.num);//11234    静态属性和方法 通过 类名.方法(属性名) 调用或访问
console.log(stu.num);//undefined    实例对象不能使用
Person.printNum();//11234
stu.printNum();//报错 stu.printNum is not a function

 

 


ES6

class Person {
    constructor(name, age) {//构造函数
        this.name = name;  /*属性*/
        this.age = age;
        this.sex = "男";
        this.study = function () {
            console.log(this.name + ‘在运动‘);
        }
    }

    static num = 11234;//定义静态属性 
    static printNum() {//定义静态方法
        console.log(this.num);
    }
    exercise() {//共有的函数
        console.log(this.name + ‘在锻炼‘);
    }
}
var stu = new Person(‘张三‘, 20);
console.log(stu);//Person { name: ‘张三‘, age: 20, sex: ‘男‘, study: [Function] }
console.log(stu.sex);//
stu.study();//张三在运动
stu.exercise(); //张三在锻炼

console.log(Person.num);//11234    
console.log(stu.num);//undefined    
Person.printNum();//11234
stu.printNum();//报错 stu.printNum is not a function

 

注:两种方式都能成功的声明一个Person类,现在一般就用ES6方式,但是其实内部是用ES5的方式运行的,ES6方式相当于一个语法糖。

 

  关于new

    每次使用new关键字会执行的步骤:

      1、创建一个空的新对象,作为将要返回的对象实例
          var obj = {};
      2、将该对象的__proto__(隐式原型)指向创建该对象的类的原型对象
          //假设类叫做 A
          obj.__proto__ = A.prototype;
      3、将构造函数环境中的this,指向该对象
          相当于:this = obj;//意识到位,代码这样写是错误的
      4、执行构造函数中的代码,并返回刚刚创建的新对象

 注:class 定义的类,必须使用new关键字调用,不然会报错,

  new关键字总是返回一个对象,如果new 普通函数,会返回一个空对象。

 

  关于构造函数constructor

    
  constructor方法是类的默认方法,通过new命令生成对象实例时,自动调用该方法。
  在类中必须存在构造函数,可以不书写,会默认补一个空构造constructor{}


  返回值
    构造函数也有返回值,默认返回实例对象(即this),可以更改,若改为基本数据类型的返回值,则不影响函数。
    若改为复杂数据类型的返回值,则会直接将该返回值直接返回,会导致返回值不再是原来类的实例对象,

 

三、面向对象三大特征


1、封装

  隐藏内部细节,不暴露在外面;做到信息的隐藏,不关心具体怎样实现的,只需要知道能达到效果就行了。

  具体表现形式:
      函数    是对于算法的封装
      类    是对于属性和方法的封装

2、继承

  子类继承父类的所有属性和方法,并且扩展自己的属性和行为。

  类与类之间的关系:is a(是一个)  例如:学生——>人类

 

  实现

 

function Student(){
    Person.call(this);    //对象冒充实现继承 对象冒充可以继承构造函数里面的属性和方法   但是没法继承原型链上面的属性和方法
}
//原型链实现继承  原型链实现继承:可以继承构造函数里面的属性和方法 也可以继承原型链上面的属性和方法
Student.prototype = Object.assigin(Person.prototype);   //与拷贝类似,但不同
//Student.prototype=new Person();   //这种方法功能是能实现,但是太矬了
// Student.prototype = Object.create(Person.prototype); //没有将create的储存起来,只是利用原型链找到有属性的父类

改变this指向的三个函数实例方法

var name = 99;
function foo(a, b) {
    console.log(this.name, a, b);//this本来指向全局的 name=99
}
let zs = {
    name: "zs"
}
let ls = {
    name: "ls"
}
//call方法,可以指定函数内部this的指向(即函数执行时所在的作用域),然后在所指定的作用域中,调用该函数第一个参数(对象)为指向。
foo.call(ls, 1, 2);//ls 1 2
//apply方法的作用与call方法类似,也是改变this指向,然后再调用该函数。唯一的区别就是,它接收一个数组作为函数执行时的参数
foo.apply(zs, [2, 3]);//zs 2 3
//bind方法用于将函数体内的this绑定到某个对象,然后返回一个新函数。这个要主动调用
let bar = foo.bind(zs);
bar(3,4);//zs 3 4

 


  ES6

// 图形祖宗
        class Graph {
            constructor(color, top, left) {
                this.color = color;
                this.top = top;
                this.left = left;
                this.node = null;
            }
            creatNode() { }
            addNode() {
                document.body.appendChild(this.node);
            }
        }

 

// 矩形
        class Rectangle extends Graph {
            constructor(color, top, left, lengthX, lengthY) {
                super(color, top, left);
                this.lengthX = lengthX;
                this.lengthY = lengthY;
            }
            creatNode() {
                this.node = document.createElement(`div`);
                this.node.style.position = "absolute";
                this.node.style.background = this.color;
                this.node.style.top = this.top + "px";
                this.node.style.left = this.left + "px";
                this.node.style.width = this.lengthX + "px";
                this.node.style.height = this.lengthY + "px";
            }
        }

 

  extends 扩展  从子类,扩展到父类  即继承父类

  super 超类  继承父类  用于调用父亲的构造,必须与extends关键字一起使用,且只能在构造函数中所有的this之前书写


3、多态


方法(行为)  相同的行为不同的实现  传的参数不一样,决定了实现方法不一样


代码中的多态:  
    重载  参数的不同,实现不同  js自带,因为js没有严格的指定参数的个数类型


    重写  父亲的行为,儿子重写一个覆盖了

 

四、this和原型对象

this指向问题

1、箭头函数中,没有自己的this,它借用声明箭头函数处(环境中的)的this(就固定这个值)

2、谁调用,指向谁

3、事件监听函数中,this指向,绑定监听函数的那一个元素节点(谁绑,指向谁)

4、当一个函数没有被其他对象调用时,(普通调用),this指向全局对象(严格模式下面是undefined)

5、在执行构造函数时(new),this指向新创建的对象

 

原型对象


规则:

所有function创建的对象,都叫做函数对象(函数)

所有函数对象上都有一个原型对象,prototype

所有对象上都有一个隐式原型对象,__proto__,指向创建该对象的构造函数的原型对象

所有原型对象上都有一个constructor对象,指向该原型对象所在的构造函数本身

或者这样说:

对象可以分为函数对象(所有直接通过function创建的对象)和一般对象(其他对象),所有的对象在创建时,都会创建一个对应的原型对象

函数对象的原型对象为:函数.prototype,函数.prototype上都有一个constructor对象,指向该原型对象所在的构造函数本身

一般对象的原型对象被称为隐式(和函数对象的原型对象相区分)原型对象: 对象.__proto__,指向创建该对象的构造函数的原型对象


原型链:

一个对象通过自身的__proto__,一直向上链接,直到null,所构成的的链条,就是原型链


原型链作用:用来找属性

当一个对象在访问自身某个属性时,会先在自己身上找,没有找到就通过原型链(__proto__),层层向上找,直到找完为止

若找完整个原型链都没有找到,那么该属性的值就是,undefind。

 

 
 

以上是关于面向对象JS ES5/ES6 类的书写 继承的实现 new关键字执行 this指向 原型链的主要内容,如果未能解决你的问题,请参考以下文章

JS面向对象,原型,继承

JS面向对象 关于类的封装继承多态

昼猫笔记 JavaScript -- 面向对象(II)- 继承

Python基础(二十四):面向对象核心知识

Python基础(二十四):面向对象核心知识

面向对象总结