Skip to content

Instantly share code, notes, and snippets.

@democ
Created April 1, 2014 00:51
Show Gist options
  • Save democ/9905662 to your computer and use it in GitHub Desktop.
Save democ/9905662 to your computer and use it in GitHub Desktop.

都说JS是一种class free语言.但是目前对这个认知还不是很深刻.以下一种模拟classd的方法,平时也经常用:

 var Car = function(name){
  this.name = name;
 } 

 Car.prototype.run = function() {
  console.log('Woonnnnnnn');
 }

 myCar = new Car('BYD');
 myCar.name // 'BYD'
 myCar.run // 'Woonnnnnnn'

这里发生的事情是这样的:

(1)使用关键词 new来调用函数
这种调用方式称为Construct Invocation.

任何函数使用Construct Invocation调用方式都会新生成一个object. 这个新生成的对象就是指向被调用函数(即Car)的prototype.所以这里myCar才会有run这个方法可以调用.

被调用函数的函数体中的this指向这个新创建的对象.所以这里myCar会有name属性.

从function的创建说起

(tip: 所有的object都是由construcot function创建) 其实每个function创建时都和两个看不见的属性息息相关: 1)the function's context 2)the code that implements the function's behavior.

每个function创建时都会有一个prototype属性(这个很重要).你可以这样拿到它:

  function greet(){
    alert('Heloo, my friend.');
  }

  greet.prototype
  // Object {}

  greet.prototype.constructor
  // function(){alert('Hello, my friend');}

它的值是一个object.这个object有一个名为constractor的key,值是这个function本身.(注:这里的prototype和Function.prototype有所不同)

prototype 继承链

很久以前就听过这句话:

当你读取一个objectA的属性时,如果它本身没有定义这个属性,就会继续往上到objectA.prototype所对应的object的上去找,如果还没找到则继续直到Object.prototype所对指向的那个object.

现在我们来找看看objectA的prototype是怎么来的吧.

在JavaScript中所有的object都是由构造函数构造出来的(其实这也给js一直声称一个object可以从另一个object继承属性带来疑惑的地方).在构造的过程中JavaScript会读取这个构造函数的prototype属性.然后这个prototype对应的object就会成为新的object.通过JavaScript中this值在不同调用方法下对应不同对象的特性,这个新object可以通过这种方式来继承原来object.来模拟class.

在JavaScript中模拟类:

面向对象的语言中,我们使用类来创建一个自定义对象 类定义了一类事物公共的行为和方法;而实例则是类的一个具体实现。面向对象编程有三个重要的概念 - 封装、继承和多态. 如何在class free的JavaScript中模拟类和继承.

  // 构造函数
  function Person(name, set){
    this.name = name;
    this.sex = sex;
  }

  // 定义Person的原型,原型中的属性可以被自定义对象引用
  Person.prototype = {
    getName: function(){
      return this.name;
    },
    getSex: function(){
      return this.sex;
    }
  }

我们把Person称为构造函数,也就是创建自定义对象的函数.可以看出,JavaScript通过构造函数和原型的方式模拟了类的功能.创建自定义对象:

  var chen = new Person('ChenLiang', 'man');
  var yu = new Person('')

在JavaScript中模拟继承

下面的例子将创建一个雇员类Employee,它从Person继承了原型prototype中所有属性.

  function Employee(name, sex, employeeID) {
      this.name = name;
      this.sex = sex;
      this.employeeID = employeeID;
  }
  // 将Employee的原型指向Person的一个实例
  // 因为Person的实例可以调用Person原型中的方法, 所以Employee的实例也可以调用Person原型中的所有属性。
  Employee.prototype = new Person();
  Employee.prototype.getEmployeeID = function() {
      return this.employeeID;
  };
  var zhang = new Employee("ZhangSan", "man", "1234");
  console.log(zhang.getName()); // "ZhangSan

上面的继承实现很粗糙,并存在问题:

  • 在创建Employee构造函数和原型(以后简称类)时,就对Person进行了实例化,这是不合适的。
  • Employee的构造函数没法调用父类Person的构造函数,导致在Employee构造函数中对name和sex属性的重复赋值。
  • Employee中的函数会覆盖Person中的同名函数,没有重载的机制(和上一条是一个类型的问题)。
  • 创建JavaScript类的语法过于零散,不如C#/Java中的语法优雅。
  • 实现中有constructor属性的指向错误,这个会在第二篇文章中讨论。

防止误操作

防止构造函数被非new方式调用导致污染全局变量的问题:

  if( !(this instanceof arguments.callee) )
    throw new Error('Constructor called as a function!')

John Resig模拟类的方式

simple-class-initantiation

simple-JavaScript-Inheritance

John Resig模拟类的继承

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment