zoukankan      html  css  js  c++  java
  • JavaScript面向对象编程

    基本都是廖学峰老师JavaScript教程的内容。先前看也似是懂了,自己稍微理下作复习。

    1、原型链

    JavaScript 的原型链和 Java 的 Class 区别就在, 它没有“Class”的概念, 所有对象都是实例,所谓继承关系不过是把一个对象的原型指向另一个对象而已。

    Student是一个现有的对象,不同于C++,Python中类对象的概念,Student更符合实例对象。Studentname属性和run()方法。xm只有name属性,Studentxm是同等地位的实例对象。xm.__proto__ = Student;xm的原型指向了对象Student,看上去 xm仿佛是从Student继承下来的。xm可以调用Studentrun()方法。

    var Student = {
        name: 'Robert',
        run: function() {
            console.log(this.name + ' is running...');
        }
    };
    
    var xm = {
        name: 'xiaoming'
    }
    
    xm.__proto__ = Student; 
    
    console.log(xm.name);
    xm.run();
    console.log(Student.name);
    Student.run();
    
    // run output:
    // xiaoming
    // xiaoming is running...
    // Robert
    // Robert is running...
    

    上述代码仅用于演示目的。在编写 JavaScript 代码时,不要直接用 obj.__proto__去改变一个对象的原型,并且,低版本的IE也无法使用__proto__


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

    xm对象的__proto__属性是{ name: 'Robert', run: [Function: run] },而没有prototype属性。

    var Student = {
        name: 'Robert',
        run: function() {
            console.log(this.name + ' is running...');
        }
    };
    function createStudent(name) {
        // 传入一个原型对象,创建一个新的原型对象
        var s = Object.create(Student);
        s.name = name;
        return s;
    }
    var xm = createStudent('xiaoming');
    xm.run()    // xiaoming is running...
    // xm 对象的__proto__ 和 prototype
    console.log(xm.__proto__)	//{ name: 'Robert', run: [Function: run] }
    console.log(xm.prototype)   // undefined
    
    // 原型链
    console.log(xm.__proto__ === Student);
    console.log(Student.__proto__ === Object.__proto__.__proto__);
    console.log(Object.__proto__.__proto__.__proto__ === null);
    

    对象xm原型链为:
    xm --> Student.__proto__ --> Object.__proto__ --> null

    2、构造函数

    除了直接用{ ... }创建一个对象外,JavaScript还可以用一种构造函数的方法来创建对象。它的用法是,先定义一个构造函数,再用关键字new来调用这个函数,并返回一个对象。构造函数和普通函数并无差别。如果不写new,它就是一个普通函数,它返回undefined。但是,如果写了new,它就变成了一个构造函数,它绑定的this指向新创建的对象,并默认返回this,也就是说,不需要在最后写return this

    function Student(name) {
        this.name = name;
        this.hello = function () {
            console.log('Hello, ' + this.name);
        };
    }
    
    var jack = new Student('jack');
    
    console.log(jack.name);   //jack
    jack.hello();     //Hello, jack
    
    console.log(jack.__proto__ === Student.prototype)
    console.log(Student.prototype.__proto__ === Object.prototype)
    console.log(Object.prototype.__proto__ === null)
    

    对象jack的原型链是:
    jack --> Student.prototype --> Object.prototype --> null
    用new Student()创建的对象还从原型上获得了一个constructor属性,它指向函数Student本身:

    console.log(jack.constructor === Student.prototype.constructor) // true
    console.log(Student.prototype.constructor === Student) // true
    console.log(Object.getPrototypeOf(jack) === Student.prototype) // true
    console.log(jack instanceof Student) // true
    

    ### 3、__proto__和prototype **JavaScript 对每个创建的对象都会设置一个原型,指向它的原型对象。**在JS中万物皆对象。字符串String是对象,数组([])是对象,对象({})是对象,函数方法(Function)是对象。下面分别看下一般对象 和 函数对象 的原型连。 String对象的原型链: 和之前的对象`xm`类似字符串对象`str`也只有`__proto__`属性,没有`prototype`属性。 `str`的原型链是: **`str --> String.prototype --> Object.prototype --> null`** ``` var str = 'aaaaaaaa'

    //对象的__proto__ 和 prototype
    console.log(str.proto) //[String: '']
    console.log(str.prototype) //undefined

    console.log(typeof(str.proto)) //object

    // 原型链
    console.log(str.proto === String.prototype) //true
    console.log(String.prototype.proto === Object.prototype) //true
    console.log(Object.prototype.proto === null) //true

    // 构造函数
    console.log(str.constructor === String.prototype.constructor) //true
    console.log(String.prototype.constructor === String) //true
    console.log(Object.prototype.constructor === Object) //true

    // 类型
    console.log(typeof(String.prototype)) //object
    console.log(typeof(String.prototype.proto)) //object
    console.log(typeof(Object.prototype)) //object
    console.log(typeof(Object.prototype.proto)) //object

    
    函数对象的原型链,函数对象不仅有`__proto__`,还有属性`prototype`。
    那么`Function()`,`Object()`和前面的`String()`是函数对象,因为它们有`prototype`属性。
    通过`new Foo()`构建的对象`foo_object`的原型链是:
    **`foo_object --> Foo.prototype --> Object.prototype --> null`**
    函数`Foo()`也是一个对象,它的原型链是:
    **`Foo() --> Function.prototype --> Object.prototype --> null`**
    

    function Foo() {
    return 0;
    }

    var foo_object = new Foo();

    // 函数对象的__proto__ 和 prototype
    console.log(Foo.proto) //[Function]
    console.log(Foo.prototype) //Foo {}

    console.log(typeof(Foo.proto)) //function
    console.log(typeof(Foo.prototype)) //object

    // 原型链
    console.log(foo_object.proto === Foo.prototype); // true
    console.log(Foo.prototype.proto === Object.prototype); // true
    console.log(Object.prototype.proto === null) // true

    console.log(Foo.proto === Function.prototype); // true
    console.log(Function.prototype.proto === Object.prototype); // true
    console.log(Object.prototype.proto === null) // true

    // 构造函数
    console.log(Foo.constructor === Function.prototype.constructor) //true
    console.log(Function.prototype.constructor === Function) //true
    console.log(Object.prototype.constructor === Object) //true

    // 类型
    console.log(typeof(Function.prototype)) //function
    console.log(typeof(Function.prototype.proto)) //object
    console.log(typeof(Object.prototype)) //object
    console.log(typeof(Object.prototype.proto)) //object

    **1).所有对象有属性`__proto__`,指向对象的原型对象。
    2).函数对象除了有属性`__proto__`,还有属性`prototype`,`prototype`属性指向(构造)函数对象共享的属性和方法(即一个由此构造函数构造而来的对象可以继承prototype指向的属性及方法),`prototype`属性使您有能力向对象添加属性和方法。其中有一个`constructor`属性指向函数对象本身(`String.prototype.constructor === String`,`Function.prototype.constructor === Function`)。**
    
    ![](http://images2015.cnblogs.com/blog/490092/201703/490092-20170309225944734-1481442121.jpg)
    
    
    
    展示下如何使用 prototype 属性来向对象添加属性:
    1)通过`Student.prototype`的方式向函数对象添加了`age`和`hello()`属性,后续构建的对象`jack`和`sara`都继承了所增属性。
    2)`hello()`属性分别在`sara`对象构建之前和`jack`对象构建之后添加的,但继承得到的`hello()`是一样的。那么在JS中一个已构建的对象,还可以通过其原型对象“不着痕迹”的扩展属性。
    

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

    Student.prototype.age = 23;

    var jack = new Student('jack');

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

    var sara = new Student('sara')

    console.log(jack.age); //23
    jack.hello(); //Hello, jack!

    console.log(sara.age) //23
    sara.hello() //Hello, sara!

    console.log(sara.hello === jack.hello) //true

    <br/>
    ### 4、原型继承
    JavaScript由于采用原型继承,我们无法直接扩展一个Class,因为不存在Class这种类型。
    `Student`的构造函数
    

    function Student(props) {
    this.name = props.name || 'Unnamed';
    }

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

    如果要基于`Student`扩展出`PrimaryStudent`,可以先定义出`PrimaryStudent`:
    

    function PrimaryStudent(props) {
    // 调用Student构造函数,绑定this变量:
    Student.call(this, props);
    this.grade = props.grade || 1;
    }

    但是,调用了`Student`构造函数不等于继承了`Student`,`PrimaryStudent`创建的对象的原型链还是:
    `new PrimaryStudent() --> PrimaryStudent.prototype --> Object.prototype --> null`
    要想办法把原型链修改为:
    **`new PrimaryStudent() --> PrimaryStudent.prototype --> Student.prototype --> Object.prototype --> null`**
    这样,原型链对了,继承关系就对了。新的基于`PrimaryStudent`创建的对象不但能调用`PrimaryStudent.prototype`定义的方法,也可以调用`Student.prototype`定义的方法。
    可借助一个中间对象来实现正确的原型链,中间对象可以用一个空函数F来实现,原型指向`Student.prototype`。代码如下:
    

    // Student构造函数:
    function Student(props) {
    this.name = props.name || 'Unnamed';
    }

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

    // PrimaryStudent构造函数:
    function PrimaryStudent(props) {
    Student.call(this, props);
    this.grade = props.grade || 1;
    }
    // 中间对象-空函数F():
    function F() {
    }
    // step1:把F的原型指向Student.prototype:
    F.prototype = Student.prototype;

    // step2:把PrimaryStudent的原型指向一个新的F对象,F对象的原型正好指向Student.prototype:
    PrimaryStudent.prototype = new F(); //PrimaryStudent.prototype.proto === F.prototype === Student.prototype

    // step3:把PrimaryStudent原型的构造函数修复为PrimaryStudent:
    PrimaryStudent.prototype.constructor = PrimaryStudent;

    // 继续在PrimaryStudent原型(就是new F()对象)上定义方法:
    PrimaryStudent.prototype.getGrade = function () {
    return this.grade;
    };

    // 创建xiaoming:
    var xiaoming = new PrimaryStudent({
    name: '小明',
    grade: 2
    });
    xiaoming.name; // '小明'
    xiaoming.grade; // 2

    // 验证原型链:
    console.log(xiaoming.proto === PrimaryStudent.prototype); // true
    console.log(PrimaryStudent.prototype.proto === Student.prototype); // true
    console.log(Student.prototype.proto === Object.prototype); // true
    // 验证继承关系:
    console.log(xiaoming instanceof PrimaryStudent); // true
    console.log(xiaoming instanceof Student); // true

    <br/>
    ### 5、class继承
    新的关键字class从ES6开始正式被引入到JavaScript中。
    如果用新的`class`关键字来编写Student,而且可以直接通过`extends`来实现继承,原型继承的中间对象,原型对象的构造函数等都不需要考虑了。
    使用`class`定义的对象如`Student`还是函数对象,`class`的目的就是让定义类更简单。`class`的定义包含了构造函数`constructor`和定义在原型对象上的函数`hello()`(注意没有function关键字),这样就避免了`Student.prototype.hello = function () {...}`这样分散的代码。
    

    // class关键
    class Student {
    constructor(name) {
    this.name = name;
    }

    hello() {
        console.log('Hello, ' + this.name + '!');
    }
    

    }
    console.log(typeof(Student)); //function

    var xiaoming = new Student('小明');
    xiaoming.hello(); // Hello, 小明!

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

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

    }

    var xiaohong = new PrimaryStudent('小红', 24)
    xiaohong.hello() // Hello, 小红!
    xiaohong.myGrade() // I am at grade 24

    ES6引入的class和原有的JavaScript原型继承有什么区别呢?实际上它们没有任何区别,class的作用就是让JavaScript引擎去实现原来需要我们自己编写的原型链代码。简而言之,用class的好处就是极大地简化了原型链代码。
  • 相关阅读:
    BZOJ 3668: [Noi2014]起床困难综合症【贪心】
    浅谈错排公式的推导及应用
    Python爬虫笔记(一):爬虫基本入门
    机器理解大数据秘密:聚类算法深度剖析
    想了解概率图模型?你要先理解图论的基本定义与形式
    MATLAB命令大全+注释小结
    【批处理学习笔记】第二十九课:ASCII码
    【批处理学习笔记】第二十八课:声音和控制
    【批处理学习笔记】第二十七课:视窗
    【批处理学习笔记】第二十六课:返回值
  • 原文地址:https://www.cnblogs.com/elie/p/6528409.html
Copyright © 2011-2022 走看看