JavaScript中的继承

开始之前

  • JavaScript使用场景
    • Web前端
    • 服务器
    • PC跨平台应用
    • 移动端:React Native Weex 微信小程序

Jeff Atwood: Any application that can be written in JavaScript, will eventually be written in JavaScript.

  • JavaScript的世界一切都是对象,函数也是对象!对象是一等公民
  • JavaScript动态语言:可以方便的在运行时给对象添加或删除属性、方法

动态性比较:JavaScript > Objective-C > Java > Swit > C++

  • JavaScript多范式语言

    • 过程式
    • 面向对象
    • 函数式
    • 响应式
    • ....
  • JavaScript调试

    • 浏览器环境下运行:Chrome浏览器开发者工具
    • Node环境运行:https://nodejs.org/zh-cn/
  • Hello world

    var jsObject = {name:'Andrew'}
    
    console.log(jsObject) //{name: "Andrew"}
    console.log(jsObject.name) //Andrew
    console.log(typeof jsObject) //object
    console.log(jsObject.age) //undefined
    jsObject.age = 30
    console.log(jsObject.age) //30

    function foo(){
        return 'I am foo.'
    }
    
    jsObject.speak = foo
    jsObject.speak() //"I am foo."
    delete jsObject.speak
    console.log(jsObject.speak) //undefined
    
    console.log(typeof foo) //function
    foo.customTag = 'HomeCredit';
    console.log(foo.customTag) //HomeCredit
    
    function foo2(){ 
        return 2018
    }
    foo.customFunc = foo2
    foo.customFunc() //2018

继承

  • JavaScript基于原型继承的语言:
    • prototype(原型):只有函数对象拥有此字段,创建一个函数就有一个对应的函数原型对象,prototype指向函数原型对象
    • __proto__(原型链):任何对象都拥有__proto__这个字段,用来在运行时查找调用的属性方法,__proto__也指向某个对象,以此构成查找链。
    • 通过函数创建的对象的__proto__字段,自动指向函数的原型对象
    function Foo(){};
    var foo = new Foo(); //只有函数才能使用new关键字
    foo.__proto__ == Foo.prototype

tips:JavaScript的世界一切都是对象

  • 代码
function Base(){
    
}; //声明Base类,同时也是构造函数constructor,
console.log(Base) //ƒ Base(){}

console.log(Base.prototype)
/*
Base.prototype
    constructor:ƒ Base()
    __proto__:Object
*/
console.log(Base.__proto__) //ƒ () { [native code] }, Function.prototype
Base.__proto__ == Function.prototype //true

var base = new Base();
base.constructor //ƒ Base(){}
base.constructor == Base //true
base.__proto__ == Base.prototype //true

Object.prototype
/*
constructor:ƒ Object()
hasOwnProperty:ƒ hasOwnProperty()
isPrototypeOf:ƒ isPrototypeOf()
propertyIsEnumerable:ƒ propertyIsEnumerable()
toLocaleString:ƒ toLocaleString()
toString:ƒ toString()
valueOf:ƒ valueOf()
__defineGetter__:ƒ __defineGetter__()
__defineSetter__:ƒ __defineSetter__()
__lookupGetter__:ƒ __lookupGetter__()
__lookupSetter__:ƒ __lookupSetter__()
get __proto__:ƒ __proto__()
set __proto__:ƒ __proto__()
*/

Function.prototype // ƒ () { [native code] }
Object.__proto__ == Function.prototype //true
Function.prototype.__proto__ == Object.prototype //true
Base.__proto__ == Function.prototype //true

base.toString(); //"[object Object]" 

Base.prototype = {
    method1:function(){
        return "Base method1";
    },
    
    method2:function(){
        return "Base method2";
    }
};
base.method1(); //undefined
base.method2(); //undefined

var base_new = new Base();
base_new.method1(); //"Base method1"
base_new.method2(); //"Base method2"

function Derived(){}
Derived.prototype = {
    method1:function(){return "Derived method1";},
    method3:function(){return "Derived method3";}
}
Derived.prototype.__proto__ = Base.prototype;
var derived = new Derived();
derived.method1(); //"Derived method1"
derived.method2(); //"Base method2" 
derived.method3(); //"Derived method3"
  • 图例
  • ES6
    • 新一代的js版本
    • 添加了各种语法和语法糖
    • 文档:阮一峰教程
    • Babel: ES6代码转ES5
class Base {
  foo(){
    return "i am base"
  }
}

class Derived extends Base {
  foo(){
    return "i am Derived"
  }
}

转码后

"use strict";

var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }();

function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; }

function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; }

function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }

var Base = function () {
  function Base() {
    _classCallCheck(this, Base);
  }

  _createClass(Base, [{
    key: "foo",
    value: function foo() {
      return "i am base";
    }
  }]);

  return Base;
}();

var Derived = function (_Base) {
  _inherits(Derived, _Base);

  function Derived() {
    _classCallCheck(this, Derived);

    return _possibleConstructorReturn(this, (Derived.__proto__ || Object.getPrototypeOf(Derived)).apply(this, arguments));
  }

  _createClass(Derived, [{
    key: "foo",
    value: function foo() {
      return "i am Derived";
    }
  }]);

  return Derived;
}(Base);