1

深入浅出javaScript中的Class

 3 years ago
source link: http://liguixing.com/archives/1410
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

ES6提供了更接近传统语言的写法,引入了Class(类)这个概念,作为对象的模板。通过class关键字,可以定义类。基本上,ES6的class可以看作 只是一个语法糖,它的绝大部分功能,ES5都可以做到,新的class写法只是让对象原型的写法更加清晰、更像面向对象编程的语法而已.

<span class="pl-k">function</span> <span class="pl-v">Point</span><span class="pl-kos">(</span><span class="pl-s1">x</span><span class="pl-kos">,</span> <span class="pl-s1">y</span><span class="pl-kos">)</span> <span class="pl-kos">{</span>  
    <span class="pl-smi">this</span><span class="pl-kos">.</span><span class="pl-c1">x</span> <span class="pl-c1">=</span> <span class="pl-s1">x</span><span class="pl-kos">;</span>  
    <span class="pl-smi">this</span><span class="pl-kos">.</span><span class="pl-c1">y</span> <span class="pl-c1">=</span> <span class="pl-s1">y</span><span class="pl-kos">;</span> 
<span class="pl-kos">}</span>
 
<span class="pl-v">Point</span><span class="pl-kos">.</span><span class="pl-c1">prototype</span><span class="pl-kos">.</span><span class="pl-en">toString</span> <span class="pl-c1">=</span> <span class="pl-k">function</span> <span class="pl-kos">(</span><span class="pl-kos">)</span> <span class="pl-kos">{</span>  
    <span class="pl-k">return</span> <span class="pl-s">'('</span> <span class="pl-c1">+</span> <span class="pl-smi">this</span><span class="pl-kos">.</span><span class="pl-c1">x</span> <span class="pl-c1">+</span> <span class="pl-s">', '</span> <span class="pl-c1">+</span> <span class="pl-smi">this</span><span class="pl-kos">.</span><span class="pl-c1">y</span> <span class="pl-c1">+</span> <span class="pl-s">')'</span><span class="pl-kos">;</span> 
<span class="pl-kos">}</span>
 
 
<span class="pl-c">//定义类 </span>
<span class="pl-k">class</span> <span class="pl-v">Point</span> <span class="pl-kos">{</span>  
    <span class="pl-en">constructor</span><span class="pl-kos">(</span><span class="pl-s1">x</span><span class="pl-kos">,</span> <span class="pl-s1">y</span><span class="pl-kos">)</span> <span class="pl-kos">{</span>    
        <span class="pl-smi">this</span><span class="pl-kos">.</span><span class="pl-c1">x</span> <span class="pl-c1">=</span> <span class="pl-s1">x</span><span class="pl-kos">;</span>    
        <span class="pl-smi">this</span><span class="pl-kos">.</span><span class="pl-c1">y</span> <span class="pl-c1">=</span> <span class="pl-s1">y</span><span class="pl-kos">;</span>  
    <span class="pl-kos">}</span>
 
    <span class="pl-en">toString</span><span class="pl-kos">(</span><span class="pl-kos">)</span> <span class="pl-kos">{</span>    
        <span class="pl-k">return</span> <span class="pl-s">'('</span> <span class="pl-c1">+</span> <span class="pl-smi">this</span><span class="pl-kos">.</span><span class="pl-c1">x</span> <span class="pl-c1">+</span> <span class="pl-s">', '</span> <span class="pl-c1">+</span> <span class="pl-smi">this</span><span class="pl-kos">.</span><span class="pl-c1">y</span> <span class="pl-c1">+</span> <span class="pl-s">')'</span><span class="pl-kos">;</span>  
    <span class="pl-kos">}</span> 
<span class="pl-kos">}</span>
 
<span class="pl-c">// 类实际上就是方法</span>
<span class="pl-k">typeof</span> <span class="pl-v">Point</span> <span class="pl-c">// function</span>
 
<span class="pl-c">// 构造函数的prototype属性,在ES6的“类”上面继续存在。事实上,类的所有方法都定义在类的prototype属性上面。</span>
<span class="pl-k">class</span> <span class="pl-v">B</span> <span class="pl-kos">{</span><span class="pl-kos">}</span> 
<span class="pl-k">let</span> <span class="pl-s1">b</span> <span class="pl-c1">=</span> <span class="pl-k">new</span> <span class="pl-v">B</span><span class="pl-kos">(</span><span class="pl-kos">)</span><span class="pl-kos">;</span>
<span class="pl-s1">b</span><span class="pl-kos">.</span><span class="pl-c1">constructor</span> <span class="pl-c1">===</span> <span class="pl-v">B</span><span class="pl-kos">.</span><span class="pl-c1">prototype</span><span class="pl-kos">.</span><span class="pl-c1">constructor</span> <span class="pl-c">// true</span>
 
<span class="pl-c">// prototype对象的constructor属性,直接指向“类”的本身,这与ES5的行为是一致的。</span>
<span class="pl-s1">b</span><span class="pl-kos">.</span><span class="pl-c1">prototype</span><span class="pl-kos">.</span><span class="pl-c1">constructor</span> <span class="pl-c1">===</span> <span class="pl-v">B</span> <span class="pl-c">// true</span>

另外,类的内部所有定义的方法,都是不可枚举的(non-enumerable)。

<span class="pl-k">class</span> <span class="pl-v">Point</span> <span class="pl-kos">{</span>  
    <span class="pl-en">constructor</span><span class="pl-kos">(</span><span class="pl-s1">x</span><span class="pl-kos">,</span> <span class="pl-s1">y</span><span class="pl-kos">)</span> <span class="pl-kos">{</span>    
         <span class="pl-smi">this</span><span class="pl-kos">.</span><span class="pl-c1">x</span> <span class="pl-c1">=</span> <span class="pl-s1">x</span><span class="pl-kos">;</span>
         <span class="pl-smi">this</span><span class="pl-kos">.</span><span class="pl-c1">y</span> <span class="pl-c1">=</span> <span class="pl-s1">y</span><span class="pl-kos">;</span> 
    <span class="pl-kos">}</span>
 
    <span class="pl-en">toString</span><span class="pl-kos">(</span><span class="pl-kos">)</span> <span class="pl-kos">{</span>    
        <span class="pl-c">// ...  </span>
    <span class="pl-kos">}</span> 
<span class="pl-kos">}</span>
 
<span class="pl-v">Object</span><span class="pl-kos">.</span><span class="pl-en">keys</span><span class="pl-kos">(</span><span class="pl-v">Point</span><span class="pl-kos">.</span><span class="pl-c1">prototype</span><span class="pl-kos">)</span> <span class="pl-c">// [] </span>
<span class="pl-v">Object</span><span class="pl-kos">.</span><span class="pl-en">getOwnPropertyNames</span><span class="pl-kos">(</span><span class="pl-v">Point</span><span class="pl-kos">.</span><span class="pl-c1">prototype</span><span class="pl-kos">)</span> <span class="pl-c">// ["constructor","toString"]</span>
 
<span class="pl-c">// 与ES5一样,实例的属性除非显式定义在其本身(即定义在this对象上),否则都是定义在原型上(即定义在class上)。</span>
<span class="pl-k">var</span> <span class="pl-s1">point</span> <span class="pl-c1">=</span> <span class="pl-k">new</span> <span class="pl-v">Point</span><span class="pl-kos">(</span><span class="pl-c1">2</span><span class="pl-kos">,</span> <span class="pl-c1">3</span><span class="pl-kos">)</span><span class="pl-kos">;</span>
<span class="pl-s1">point</span><span class="pl-kos">.</span><span class="pl-en">toString</span><span class="pl-kos">(</span><span class="pl-kos">)</span> <span class="pl-c">// (2, 3)</span>
 
<span class="pl-s1">point</span><span class="pl-kos">.</span><span class="pl-en">hasOwnProperty</span><span class="pl-kos">(</span><span class="pl-s">'x'</span><span class="pl-kos">)</span> <span class="pl-c">// true </span>
<span class="pl-s1">point</span><span class="pl-kos">.</span><span class="pl-en">hasOwnProperty</span><span class="pl-kos">(</span><span class="pl-s">'y'</span><span class="pl-kos">)</span> <span class="pl-c">// true </span>
<span class="pl-s1">point</span><span class="pl-kos">.</span><span class="pl-en">hasOwnProperty</span><span class="pl-kos">(</span><span class="pl-s">'toString'</span><span class="pl-kos">)</span> <span class="pl-c">// false </span>
<span class="pl-s1">point</span><span class="pl-kos">.</span><span class="pl-c1">__proto__</span><span class="pl-kos">.</span><span class="pl-en">hasOwnProperty</span><span class="pl-kos">(</span><span class="pl-s">'toString'</span><span class="pl-kos">)</span> <span class="pl-c">// true</span>
 
 
<span class="pl-c">// toString方法是Point类内部定义的方法,它是不可枚举的。这一点与ES5的行为不一致。</span>
<span class="pl-k">var</span> <span class="pl-v">Point</span> <span class="pl-c1">=</span> <span class="pl-k">function</span> <span class="pl-kos">(</span><span class="pl-s1">x</span><span class="pl-kos">,</span> <span class="pl-s1">y</span><span class="pl-kos">)</span> <span class="pl-kos">{</span> <span class="pl-kos">}</span><span class="pl-kos">;</span>
<span class="pl-v">Point</span><span class="pl-kos">.</span><span class="pl-c1">prototype</span><span class="pl-kos">.</span><span class="pl-en">toString</span> <span class="pl-c1">=</span> <span class="pl-k">function</span><span class="pl-kos">(</span><span class="pl-kos">)</span> <span class="pl-kos">{</span> <span class="pl-kos">}</span><span class="pl-kos">;</span>
<span class="pl-v">Object</span><span class="pl-kos">.</span><span class="pl-en">keys</span><span class="pl-kos">(</span><span class="pl-v">Point</span><span class="pl-kos">.</span><span class="pl-c1">prototype</span><span class="pl-kos">)</span> <span class="pl-c">// ["toString"] </span>
<span class="pl-v">Object</span><span class="pl-kos">.</span><span class="pl-en">getOwnPropertyNames</span><span class="pl-kos">(</span><span class="pl-v">Point</span><span class="pl-kos">.</span><span class="pl-c1">prototype</span><span class="pl-kos">)</span> <span class="pl-c">// ["constructor","toString"]</span>
 
 
<span class="pl-s1">point</span><span class="pl-kos">.</span><span class="pl-en">hasOwnProperty</span><span class="pl-kos">(</span><span class="pl-s">'x'</span><span class="pl-kos">)</span> <span class="pl-c">// true </span>
<span class="pl-s1">point</span><span class="pl-kos">.</span><span class="pl-en">hasOwnProperty</span><span class="pl-kos">(</span><span class="pl-s">'y'</span><span class="pl-kos">)</span> <span class="pl-c">// true </span>
<span class="pl-s1">point</span><span class="pl-kos">.</span><span class="pl-en">hasOwnProperty</span><span class="pl-kos">(</span><span class="pl-s">'toString'</span><span class="pl-kos">)</span> <span class="pl-c">// false </span>

constructor方法

constructor方法是类的默认方法,通过new命令生成对象实例时,自动调用该方法。一个类必须有constructor方法,如果没有显式定义,一个空 的constructor方法会被默认添加。 constructor方法默认返回实例对象(即this),完全可以指定返回另外一个对象。

<span class="pl-k">class</span> <span class="pl-v">Foo</span> <span class="pl-kos">{</span>  <span class="pl-en">constructor</span><span class="pl-kos">(</span><span class="pl-kos">)</span> <span class="pl-kos">{</span>    <span class="pl-k">return</span> <span class="pl-v">Object</span><span class="pl-kos">.</span><span class="pl-en">create</span><span class="pl-kos">(</span><span class="pl-c1">null</span><span class="pl-kos">)</span><span class="pl-kos">;</span>  <span class="pl-kos">}</span> <span class="pl-kos">}</span>
<span class="pl-k">new</span> <span class="pl-v">Foo</span><span class="pl-kos">(</span><span class="pl-kos">)</span> <span class="pl-k">instanceof</span> <span class="pl-v">Foo</span> <span class="pl-c">// false</span>

上面代码中,constructor函数返回一个全新的对象,结果导致实例对象不是Foo类的实例。

类的构造函数,不使用new是没法调用的,会报错。这是它跟普通构造函数的一个主要区别,后者不用new也可以执行。

<span class="pl-k">class</span> <span class="pl-v">Foo</span> <span class="pl-kos">{</span>  <span class="pl-en">constructor</span><span class="pl-kos">(</span><span class="pl-kos">)</span> <span class="pl-kos">{</span>    <span class="pl-k">return</span> <span class="pl-v">Object</span><span class="pl-kos">.</span><span class="pl-en">create</span><span class="pl-kos">(</span><span class="pl-c1">null</span><span class="pl-kos">)</span><span class="pl-kos">;</span>  <span class="pl-kos">}</span> <span class="pl-kos">}</span>
<span class="pl-v">Foo</span><span class="pl-kos">(</span><span class="pl-kos">)</span> <span class="pl-c">// TypeError: Class constructor Foo cannot be invoked without 'new'</span>

静态方法

类相当于实例的原型,所有在类中定义的方法,都会被实例继承。如果在一个方法前,加上static关键字,就表示该方法不会被实例继承,而是直接 通过类来调用,这就称为“静态方法”。

<span class="pl-k">class</span> <span class="pl-v">Foo</span> <span class="pl-kos">{</span>  <span class="pl-k">static</span> <span class="pl-en">classMethod</span><span class="pl-kos">(</span><span class="pl-kos">)</span> <span class="pl-kos">{</span>    <span class="pl-k">return</span> <span class="pl-s">'hello'</span><span class="pl-kos">;</span>  <span class="pl-kos">}</span> <span class="pl-kos">}</span>
<span class="pl-v">Foo</span><span class="pl-kos">.</span><span class="pl-en">classMethod</span><span class="pl-kos">(</span><span class="pl-kos">)</span> <span class="pl-c">// 'hello'</span>
<span class="pl-k">var</span> <span class="pl-s1">foo</span> <span class="pl-c1">=</span> <span class="pl-k">new</span> <span class="pl-v">Foo</span><span class="pl-kos">(</span><span class="pl-kos">)</span><span class="pl-kos">;</span> 
<span class="pl-s1">foo</span><span class="pl-kos">.</span><span class="pl-en">classMethod</span><span class="pl-kos">(</span><span class="pl-kos">)</span> <span class="pl-c">// TypeError: foo.classMethod is not a function</span>
 
<span class="pl-c">// 父类的静态方法,可以被子类继承。</span>
<span class="pl-k">class</span> <span class="pl-v">Bar</span> <span class="pl-k">extends</span> <span class="pl-v">Foo</span> <span class="pl-kos">{</span> <span class="pl-kos">}</span>
<span class="pl-v">Bar</span><span class="pl-kos">.</span><span class="pl-en">classMethod</span><span class="pl-kos">(</span><span class="pl-kos">)</span><span class="pl-kos">;</span> <span class="pl-c">// 'hello'</span>
 
<span class="pl-c">// 静态方法也是可以从super对象上调用的。</span>
<span class="pl-k">class</span> <span class="pl-v">Bar</span> <span class="pl-k">extends</span> <span class="pl-v">Foo</span> <span class="pl-kos">{</span>  
    <span class="pl-k">static</span> <span class="pl-en">classMethod</span><span class="pl-kos">(</span><span class="pl-kos">)</span> <span class="pl-kos">{</span>    
        <span class="pl-k">return</span> <span class="pl-smi">super</span><span class="pl-kos">.</span><span class="pl-en">classMethod</span><span class="pl-kos">(</span><span class="pl-kos">)</span> <span class="pl-c1">+</span> <span class="pl-s">', too'</span><span class="pl-kos">;</span>  
    <span class="pl-kos">}</span> 
<span class="pl-kos">}</span>
<span class="pl-v">Bar</span><span class="pl-kos">.</span><span class="pl-en">classMethod</span><span class="pl-kos">(</span><span class="pl-kos">)</span><span class="pl-kos">;</span>

静态方法的实现其实跟下面类似

<span class="pl-k">class</span> <span class="pl-v">Foo</span> <span class="pl-kos">{</span>  
    <span class="pl-k">static</span> <span class="pl-en">classMethod</span><span class="pl-kos">(</span><span class="pl-kos">)</span> <span class="pl-kos">{</span>    
        <span class="pl-k">return</span> <span class="pl-s">'hello'</span><span class="pl-kos">;</span>  
    <span class="pl-kos">}</span> 
<span class="pl-kos">}</span>
 
<span class="pl-c">// 等同于</span>
<span class="pl-k">function</span> <span class="pl-v">Foo</span><span class="pl-kos">(</span><span class="pl-kos">)</span><span class="pl-kos">{</span><span class="pl-kos">}</span>
<span class="pl-v">Foo</span><span class="pl-kos">.</span><span class="pl-en">classMethod</span> <span class="pl-c1">=</span> <span class="pl-k">function</span><span class="pl-kos">(</span><span class="pl-kos">)</span><span class="pl-kos">{</span>
    <span class="pl-k">return</span> <span class="pl-s">'hello'</span><span class="pl-kos">;</span>
<span class="pl-kos">}</span>

静态属性

静态属性指的是Class本身的属性,即Class.propname,而不是定义在实例对象(this)上的属性。

<span class="pl-k">class</span> <span class="pl-v">Foo</span> <span class="pl-kos">{</span> <span class="pl-kos">}</span>
<span class="pl-v">Foo</span><span class="pl-kos">.</span><span class="pl-c1">prop</span> <span class="pl-c1">=</span> <span class="pl-c1">1</span><span class="pl-kos">;</span> 
<span class="pl-v">Foo</span><span class="pl-kos">.</span><span class="pl-c1">prop</span> <span class="pl-c">// 1</span>
 
<span class="pl-c">// 语法等同于</span>
<span class="pl-k">function</span> <span class="pl-v">Foo</span><span class="pl-kos">(</span><span class="pl-kos">)</span><span class="pl-kos">{</span><span class="pl-kos">}</span>
<span class="pl-v">Foo</span><span class="pl-kos">.</span><span class="pl-c1">prop</span> <span class="pl-c1">=</span> <span class="pl-c1">1</span>

目前,只有这种写法可行,因为ES6明确规定,Class内部只有静态方法,没有静态属性。

<span class="pl-c">// 以下两种写法都无效 </span>
<span class="pl-k">class</span> <span class="pl-v">Foo</span> <span class="pl-kos">{</span>  
    <span class="pl-c">// 写法一  </span>
    <span class="pl-c1">prop</span>: <span class="pl-c1">2</span>
    <span class="pl-c">// 写法二  </span>
    <span class="pl-k">static</span> <span class="pl-c1">prop</span>: <span class="pl-c1">2</span> 
<span class="pl-kos">}</span>
<span class="pl-v">Foo</span><span class="pl-kos">.</span><span class="pl-c1">prop</span> <span class="pl-c">// undefined</span>

继承

Class之间可以通过extends关键字实现继承,这比ES5的通过修改原型链实现继承,要清晰和方便很多。

子类必须在constructor方法中调用super方法,否则新建实例时会报错。这是因为子类没有自己的this对象,而是继承父类的this对象,然后对其进行加工。如果不调用super方法,子类就得不到this对象。

<span class="pl-k">class</span> <span class="pl-v">ColorPoint</span> <span class="pl-k">extends</span> <span class="pl-v">Point</span> <span class="pl-kos">{</span>  
    <span class="pl-en">constructor</span><span class="pl-kos">(</span><span class="pl-s1">x</span><span class="pl-kos">,</span> <span class="pl-s1">y</span><span class="pl-kos">,</span> <span class="pl-s1">color</span><span class="pl-kos">)</span> <span class="pl-kos">{</span>    
        <span class="pl-smi">super</span><span class="pl-kos">(</span><span class="pl-s1">x</span><span class="pl-kos">,</span> <span class="pl-s1">y</span><span class="pl-kos">)</span><span class="pl-kos">;</span> <span class="pl-c">// 调用父类的constructor(x, y)</span>
        <span class="pl-smi">this</span><span class="pl-kos">.</span><span class="pl-c1">color</span> <span class="pl-c1">=</span> <span class="pl-s1">color</span><span class="pl-kos">;</span>  
    <span class="pl-kos">}</span>
 
    <span class="pl-en">toString</span><span class="pl-kos">(</span><span class="pl-kos">)</span> <span class="pl-kos">{</span>    
        <span class="pl-k">return</span> <span class="pl-smi">this</span><span class="pl-kos">.</span><span class="pl-c1">color</span> <span class="pl-c1">+</span> <span class="pl-s">' '</span> <span class="pl-c1">+</span> <span class="pl-smi">super</span><span class="pl-kos">.</span><span class="pl-en">toString</span><span class="pl-kos">(</span><span class="pl-kos">)</span><span class="pl-kos">;</span> <span class="pl-c">// 调用父类的toString()  </span>
    <span class="pl-kos">}</span>
<span class="pl-kos">}</span>
 
 
<span class="pl-k">class</span> <span class="pl-v">Point</span> <span class="pl-kos">{</span> <span class="pl-c">/* ... */</span> <span class="pl-kos">}</span>
<span class="pl-k">class</span> <span class="pl-v">ColorPoint</span> <span class="pl-k">extends</span> <span class="pl-v">Point</span> <span class="pl-kos">{</span>  <span class="pl-en">constructor</span><span class="pl-kos">(</span><span class="pl-kos">)</span> <span class="pl-kos">{</span>  <span class="pl-kos">}</span> <span class="pl-kos">}</span>
<span class="pl-k">let</span> <span class="pl-s1">cp</span> <span class="pl-c1">=</span> <span class="pl-k">new</span> <span class="pl-v">ColorPoint</span><span class="pl-kos">(</span><span class="pl-kos">)</span><span class="pl-kos">;</span> <span class="pl-c">// ReferenceError</span>

如果子类没有定义constructor方法,这个方法会被默认添加,代码如下。也就是说,不管有没有显式定义,任何一个子类都有constructor方法。

<span class="pl-en">constructor</span><span class="pl-kos">(</span>...<span class="pl-s1">args</span><span class="pl-kos">)</span> <span class="pl-kos">{</span>  <span class="pl-smi">super</span><span class="pl-kos">(</span>...<span class="pl-s1">args</span><span class="pl-kos">)</span><span class="pl-kos">;</span> <span class="pl-kos">}</span>

另一个需要注意的地方是,在子类的构造函数中,只有调用super之后,才可以使用this关键字,否则会报错。这是因为子类实例的构建,是基于对父 类实例加工,只有super方法才能返回父类实例。

<span class="pl-k">class</span> <span class="pl-v">Point</span> <span class="pl-kos">{</span>  
    <span class="pl-en">constructor</span><span class="pl-kos">(</span><span class="pl-s1">x</span><span class="pl-kos">,</span> <span class="pl-s1">y</span><span class="pl-kos">)</span> <span class="pl-kos">{</span>    
        <span class="pl-smi">this</span><span class="pl-kos">.</span><span class="pl-c1">x</span> <span class="pl-c1">=</span> <span class="pl-s1">x</span><span class="pl-kos">;</span>    
        <span class="pl-smi">this</span><span class="pl-kos">.</span><span class="pl-c1">y</span> <span class="pl-c1">=</span> <span class="pl-s1">y</span><span class="pl-kos">;</span>  
    <span class="pl-kos">}</span> 
 
<span class="pl-kos">}</span>
<span class="pl-k">class</span> <span class="pl-v">ColorPoint</span> <span class="pl-k">extends</span> <span class="pl-v">Point</span> <span class="pl-kos">{</span> 
    <span class="pl-en">constructor</span><span class="pl-kos">(</span><span class="pl-s1">x</span><span class="pl-kos">,</span> <span class="pl-s1">y</span><span class="pl-kos">,</span> <span class="pl-s1">color</span><span class="pl-kos">)</span> <span class="pl-kos">{</span>
        <span class="pl-smi">this</span><span class="pl-kos">.</span><span class="pl-c1">color</span> <span class="pl-c1">=</span> <span class="pl-s1">color</span><span class="pl-kos">;</span> <span class="pl-c">// ReferenceError    </span>
        <span class="pl-smi">super</span><span class="pl-kos">(</span><span class="pl-s1">x</span><span class="pl-kos">,</span> <span class="pl-s1">y</span><span class="pl-kos">)</span><span class="pl-kos">;</span>    
        <span class="pl-smi">this</span><span class="pl-kos">.</span><span class="pl-c1">color</span> <span class="pl-c1">=</span> <span class="pl-s1">color</span><span class="pl-kos">;</span> <span class="pl-c">// 正确  </span>
    <span class="pl-kos">}</span> 
<span class="pl-kos">}</span>

Class 与 function 的区别

Class不存在变量提升(hoist),这一点与ES5完全不同。

<span class="pl-k">new</span> <span class="pl-v">Foo</span><span class="pl-kos">(</span><span class="pl-kos">)</span><span class="pl-kos">;</span> <span class="pl-c">// ReferenceError </span>
<span class="pl-k">class</span> <span class="pl-v">Foo</span> <span class="pl-kos">{</span><span class="pl-kos">}</span>

ES5的继承,实质是先创造子类的实例对象this,然后再将父类的方法添加到this上面(Parent.apply(this))。ES6的继承机制完全不同,实质是 先创造父类的实例对象this(所以必须先调用super方法),然后再用子类的构造函数修改this。

常见疑问

私有方法是常见需求,但ES6不提供,只能通过变通方法模拟实现。

类和模块的内部,默认就是严格模式,所以不需要使用use strict指定运行模式。只要你的代码写在类或模块之中,就只有严格模式可用。 考虑到未来所有的代码,其实都是运行在模块之中,所以ES6实际上把整个语言升级到了严格模式。

Object.getPrototypeOf方法可以用来从子类上获取父类。因此,可以使用这个方法判断,一个类是否继承了另一个类。

<span class="pl-v">Object</span><span class="pl-kos">.</span><span class="pl-en">getPrototypeOf</span><span class="pl-kos">(</span><span class="pl-v">ColorPoint</span><span class="pl-kos">)</span> <span class="pl-c1">===</span> <span class="pl-v">Point</span> <span class="pl-c">// true</span>

new.target属性

new是从构造函数生成实例的命令。ES6为new命令引入了一个new.target属性,(在构造函数中)返回new命令作用于的那个构造函数。如果构造函数 不是通过new命令调用的,new.target会返回undefined,因此这个属性可以用来确定构造函数是怎么调用的。

<span class="pl-k">function</span> <span class="pl-v">Person</span><span class="pl-kos">(</span><span class="pl-s1">name</span><span class="pl-kos">)</span> <span class="pl-kos">{</span>  
    <span class="pl-k">if</span> <span class="pl-kos">(</span><span class="pl-k">new</span><span class="pl-kos">.</span><span class="pl-k">target</span> !== <span class="pl-c1">undefined</span><span class="pl-kos">)</span> <span class="pl-kos">{</span>    
        <span class="pl-smi">this</span><span class="pl-kos">.</span><span class="pl-c1">name</span> <span class="pl-c1">=</span> <span class="pl-s1">name</span><span class="pl-kos">;</span>  
    <span class="pl-kos">}</span> <span class="pl-k">else</span> <span class="pl-kos">{</span>    
        <span class="pl-k">throw</span> <span class="pl-k">new</span> <span class="pl-v">Error</span><span class="pl-kos">(</span><span class="pl-s">'必须使用new生成实例'</span><span class="pl-kos">)</span><span class="pl-kos">;</span>  
    <span class="pl-kos">}</span> 
<span class="pl-kos">}</span>
<span class="pl-c">// 另一种写法 </span>
<span class="pl-k">function</span> <span class="pl-v">Person</span><span class="pl-kos">(</span><span class="pl-s1">name</span><span class="pl-kos">)</span> <span class="pl-kos">{</span>  
    <span class="pl-k">if</span> <span class="pl-kos">(</span><span class="pl-k">new</span><span class="pl-kos">.</span><span class="pl-k">target</span> <span class="pl-c1">===</span> <span class="pl-v">Person</span><span class="pl-kos">)</span> <span class="pl-kos">{</span>    
        <span class="pl-smi">this</span><span class="pl-kos">.</span><span class="pl-c1">name</span> <span class="pl-c1">=</span> <span class="pl-s1">name</span><span class="pl-kos">;</span>  
    <span class="pl-kos">}</span> <span class="pl-k">else</span> <span class="pl-kos">{</span>    
        <span class="pl-k">throw</span> <span class="pl-k">new</span> <span class="pl-v">Error</span><span class="pl-kos">(</span><span class="pl-s">'必须使用new生成实例'</span><span class="pl-kos">)</span><span class="pl-kos">;</span>  
    <span class="pl-kos">}</span> 
<span class="pl-kos">}</span>
<span class="pl-k">var</span> <span class="pl-s1">person</span> <span class="pl-c1">=</span> <span class="pl-k">new</span> <span class="pl-v">Person</span><span class="pl-kos">(</span><span class="pl-s">'张三'</span><span class="pl-kos">)</span><span class="pl-kos">;</span> <span class="pl-c">// 正确 </span>
<span class="pl-k">var</span> <span class="pl-s1">notAPerson</span> <span class="pl-c1">=</span> <span class="pl-v">Person</span><span class="pl-kos">.</span><span class="pl-en">call</span><span class="pl-kos">(</span><span class="pl-s1">person</span><span class="pl-kos">,</span> <span class="pl-s">'张三'</span><span class="pl-kos">)</span><span class="pl-kos">;</span>  <span class="pl-c">// 报错</span>

需要注意的是,子类继承父类时,new.target会返回子类。

<span class="pl-k">class</span> <span class="pl-v">Rectangle</span> <span class="pl-kos">{</span>  
    <span class="pl-en">constructor</span><span class="pl-kos">(</span><span class="pl-s1">length</span><span class="pl-kos">,</span> <span class="pl-s1">width</span><span class="pl-kos">)</span> <span class="pl-kos">{</span>    
        <span class="pl-smi">console</span><span class="pl-kos">.</span><span class="pl-en">log</span><span class="pl-kos">(</span><span class="pl-k">new</span><span class="pl-kos">.</span><span class="pl-k">target</span> <span class="pl-c1">===</span> <span class="pl-v">Rectangle</span><span class="pl-kos">)</span><span class="pl-kos">;</span>
        <span class="pl-c">// ...  </span>
    <span class="pl-kos">}</span>
<span class="pl-kos">}</span>
<span class="pl-k">class</span> <span class="pl-v">Square</span> <span class="pl-k">extends</span> <span class="pl-v">Rectangle</span> <span class="pl-kos">{</span>  <span class="pl-en">constructor</span><span class="pl-kos">(</span><span class="pl-s1">length</span><span class="pl-kos">)</span> <span class="pl-kos">{</span>    <span class="pl-smi">super</span><span class="pl-kos">(</span><span class="pl-s1">length</span><span class="pl-kos">,</span> <span class="pl-s1">length</span><span class="pl-kos">)</span><span class="pl-kos">;</span>  <span class="pl-kos">}</span> <span class="pl-kos">}</span>
<span class="pl-k">var</span> <span class="pl-s1">obj</span> <span class="pl-c1">=</span> <span class="pl-k">new</span> <span class="pl-v">Square</span><span class="pl-kos">(</span><span class="pl-c1">3</span><span class="pl-kos">)</span><span class="pl-kos">;</span> <span class="pl-c">// 输出 false</span>

利用这个特点,可以写出不能独立使用、必须继承后才能使用的类。

<span class="pl-k">class</span> <span class="pl-v">Shape</span> <span class="pl-kos">{</span>  
    <span class="pl-en">constructor</span><span class="pl-kos">(</span><span class="pl-kos">)</span> <span class="pl-kos">{</span>    
        <span class="pl-k">if</span> <span class="pl-kos">(</span><span class="pl-k">new</span><span class="pl-kos">.</span><span class="pl-k">target</span> <span class="pl-c1">===</span> <span class="pl-v">Shape</span><span class="pl-kos">)</span> <span class="pl-kos">{</span>      
            <span class="pl-k">throw</span> <span class="pl-k">new</span> <span class="pl-v">Error</span><span class="pl-kos">(</span><span class="pl-s">'本类不能实例化'</span><span class="pl-kos">)</span><span class="pl-kos">;</span>    
        <span class="pl-kos">}</span>  
    <span class="pl-kos">}</span> 
<span class="pl-kos">}</span>
<span class="pl-k">class</span> <span class="pl-v">Rectangle</span> <span class="pl-k">extends</span> <span class="pl-v">Shape</span> <span class="pl-kos">{</span>  
    <span class="pl-en">constructor</span><span class="pl-kos">(</span><span class="pl-s1">length</span><span class="pl-kos">,</span> <span class="pl-s1">width</span><span class="pl-kos">)</span> <span class="pl-kos">{</span>    
        <span class="pl-smi">super</span><span class="pl-kos">(</span><span class="pl-kos">)</span><span class="pl-kos">;</span>    <span class="pl-c">// ...  </span>
    <span class="pl-kos">}</span> 
<span class="pl-kos">}</span>
<span class="pl-k">var</span> <span class="pl-s1">x</span> <span class="pl-c1">=</span> <span class="pl-k">new</span> <span class="pl-v">Shape</span><span class="pl-kos">(</span><span class="pl-kos">)</span><span class="pl-kos">;</span>  <span class="pl-c">// 报错 var y = new Rectangle(3, 4);  // 正确</span>

多继承的实现

<span class="pl-k">function</span> <span class="pl-en">mix</span><span class="pl-kos">(</span>...<span class="pl-s1">mixins</span><span class="pl-kos">)</span> <span class="pl-kos">{</span>  
    <span class="pl-k">class</span> <span class="pl-v">Mix</span> <span class="pl-kos">{</span><span class="pl-kos">}</span>
    <span class="pl-k">for</span> <span class="pl-kos">(</span><span class="pl-k">let</span> <span class="pl-s1">mixin</span> <span class="pl-k">of</span> <span class="pl-s1">mixins</span><span class="pl-kos">)</span> <span class="pl-kos">{</span>    
        <span class="pl-en">copyProperties</span><span class="pl-kos">(</span><span class="pl-v">Mix</span><span class="pl-kos">,</span> <span class="pl-s1">mixin</span><span class="pl-kos">)</span><span class="pl-kos">;</span>    
        <span class="pl-en">copyProperties</span><span class="pl-kos">(</span><span class="pl-v">Mix</span><span class="pl-kos">.</span><span class="pl-c1">prototype</span><span class="pl-kos">,</span> <span class="pl-s1">mixin</span><span class="pl-kos">.</span><span class="pl-c1">prototype</span><span class="pl-kos">)</span><span class="pl-kos">;</span>  
    <span class="pl-kos">}</span>
    <span class="pl-k">return</span> <span class="pl-v">Mix</span><span class="pl-kos">;</span> 
<span class="pl-kos">}</span>
<span class="pl-k">function</span> <span class="pl-en">copyProperties</span><span class="pl-kos">(</span><span class="pl-s1">target</span><span class="pl-kos">,</span> <span class="pl-s1">source</span><span class="pl-kos">)</span> <span class="pl-kos">{</span>  
    <span class="pl-k">for</span> <span class="pl-kos">(</span><span class="pl-k">let</span> <span class="pl-s1">key</span> <span class="pl-k">of</span> <span class="pl-v">Reflect</span><span class="pl-kos">.</span><span class="pl-en">ownKeys</span><span class="pl-kos">(</span><span class="pl-s1">source</span><span class="pl-kos">)</span><span class="pl-kos">)</span> <span class="pl-kos">{</span>    
        <span class="pl-k">if</span> <span class="pl-kos">(</span> <span class="pl-s1">key</span> !== <span class="pl-s">"constructor"</span>      
        <span class="pl-c1">&&</span> <span class="pl-s1">key</span> !== <span class="pl-s">"prototype"</span>      
        <span class="pl-c1">&&</span> <span class="pl-s1">key</span> !== <span class="pl-s">"name"</span><span class="pl-kos">)</span> <span class="pl-kos">{</span>      
            <span class="pl-k">let</span> <span class="pl-s1">desc</span> <span class="pl-c1">=</span> <span class="pl-v">Object</span><span class="pl-kos">.</span><span class="pl-en">getOwnPropertyDescriptor</span><span class="pl-kos">(</span><span class="pl-s1">source</span><span class="pl-kos">,</span> <span class="pl-s1">key</span><span class="pl-kos">)</span><span class="pl-kos">;</span>      
            <span class="pl-v">Object</span><span class="pl-kos">.</span><span class="pl-en">defineProperty</span><span class="pl-kos">(</span><span class="pl-s1">target</span><span class="pl-kos">,</span> <span class="pl-s1">key</span><span class="pl-kos">,</span> <span class="pl-s1">desc</span><span class="pl-kos">)</span><span class="pl-kos">;</span>    
        <span class="pl-kos">}</span>  
    <span class="pl-kos">}</span> 
<span class="pl-kos">}</span>

About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK