JavaScript總結3

類和模塊

類和原型

  1. JavaScript中, 類的全部實例對象都從同一個原型對象上繼承屬性的. 咱們可使用Object.create()來實現:
function range(from, to) {

  var r = Object.create(range.method);

  r.from = from;

  r.to = to;

  return r;

}

range.method = {

  includes: function(x) {

    return this.from <= x && x <= this.to;

  },

  foreach: function(f) {

    for (var x = Math.ceil(this.from); x <= this.to; x++) f(x);

  },

  toString: function() { return "(" + this.from + "..." + this.to + ")"; }

};

 

var r = range(1, 3);

// true

console.log(r.includes(2));

// 1 2 3

r.foreach(console.log);

// (1...3)

console.log(r.toString());

類和構造函數

  1. 構造函數是用來初始化新建立的對象的. 調用構造函數的一個重要特徵是: 構造函數的prototype屬性被用來作新對象的原型. 這意味着經過同一個構造函數建立的全部對象都繼承自同一個相同的對象, 所以它們都是同一個類的成員.
  2. 定義的構造函數名字首字母一般要大寫
function Range(from, to) {

  this.from = from;

  this.to = to;

}

Range.prototype = {

  includes: function(x) {

    return this.from <= x && x <= this.to;

  },

  foreach: function(f) {

    for (var x = Math.ceil(this.from); x <= this.to; x++) f(x);

  },

  toString: function() { return "(" + this.from + "..." + this.to + ")"; }

};

 

var r = new Range(1, 3);

// true

console.log(r.includes(2));

// 1 2 3

r.foreach(console.log);

// (1...3)

console.log(r.toString());
  1. instanceof用於檢查一個實例對象是否繼承至某種原型:
r instanceof Range // 若是r繼承自Range.prototype, 則返回true

這裏, instanceof並不會檢查r是否由Range()構造函數初始化而來, 而會檢查r是否繼承自Range.prototype. 因此更原始檢測原型的方法是:javascript

Range.prototype.isPrototypeOf(r) // 檢查r的原型是否爲Range.prototype

     但上例中存在必定的錯誤, 當咱們執行如下代碼時候:java

r.constructor.prototype == Range.prototype ==> false

     這裏是false, 是由於咱們並無給Range的constructor增長Range.瀏覽器

 

  1. 每一個函數都具備constructor屬性, 用於指明此函數是若是構建的:
var F = function(){}

F.constructor == F.prototype.constructor ==> false

F.constructor ==>

function Function() { [native code] }

F.prototype.constructor ==>

function (){}

因此, 針對咱們以前所編寫的Range.prototype, 咱們因爲給Range.prototype從新賦值了, 因此須要添加上constructor:閉包

Range.prototype = {

  constructor: Range,

  ...

}

或者咱們對Range.prototype進行擴充, 則無需添加constructor:app

Range.prototype.includes = function(x) { ... }

JavaScript中的Java式繼承

  1. Range的例子中完美的詮釋了繼承: 構造函數對象(Range), 原型對象(Range.prototype), 實例對象(new Range(1, 3)).
  2. 其中, 原型對象中的全部方法和字段, 均會被構造函數對象中同名的方法和字段所覆蓋.
  3. 每一個實例對象具備本身特有的函數和方法(來自構造函數對象中所定義的), 而共享的函數和方法來自原型對象中.
  4. 因此, 通常將通用的方法放在原型對象中, 而原型對象中一般不存放字段.

類的擴充

     JavaScript中基於原型的繼承機制是動態的: 對象從其原型繼承屬性, 若是建立對象以後原型的屬性發生改變, 也會影響到繼承這個原型的全部實例對象.框架

var o = {

  show: function() {

    console.log("show");

  }

};

var sub_o = Object.create(o);

o.play = function() {

  console.log("play");

};

// play

sub_o.play();

// show play

for (var k in sub_o) {

  console.log(k);

}

     這裏, 給原型添加屬性, 默認狀況下是可枚舉的; 在ECMA5下, 可使用Object.defineProperty()設置爲不可枚舉, 但不能保證所運用的Web瀏覽器支持其defineProperty().函數

     因此, 通常咱們不推薦給Object.prototype添加方法, 或者給具體的類如String.prototype/Array.prototype添加方法, 也是基於這種考慮的.this

類和類型

  1. instanceof: 若是o繼承自c.prototype, 則表達式o instanceof c值爲true.

構造函數是類的公共標識, 但原型是惟一的標識. 儘管instanceof運算符的右操做數是構造函數, 但計算過程其實是檢測了對象的繼承關係, 而不是檢測建立對象的構造函數.spa

  1. isPrototypeOf: 檢測對象的原型鏈上是否存在某個特定的原型對象:
range.methods.isPrototypeOf(r); // range.method 是原型對象

instanceof/isPrototypeOf的不足之處在於兩點: 1是咱們沒法確切知道(o instanceof c)中o的具體類名; 2是在多窗口多框架的子頁面中, Array()並不相等.prototype

  1. constructor: 用於指明對象是如何構建的.

備註: instanceof和constructor都沒法用來檢測對象是由於, 它們在多個執行上下文中是不一樣的.

  1. 構造函數的名字: 若是一個函數具備名字, 則不管在不一樣的上下文中, 它們均是相同的:
function type(o) {

  var t, c, n;

  if (o === null) return "null";

  if (o !== o) return "nan";

  if ((t = typeof o) !== "object") return t;

  if ((c = classof(o)) !== "Object") return c;

  if (o.constructor && typeof o.constructor === "function"

      && (n = o.constructor.getName())) return n;

 

  return "Object";

}

 

function classof(o) {

  return Object.prototype.toString.call(o).slice(8, -1);

}

 

Function.prototype.getName = function() {

  if ("name" in this) return this.name;

  return this.name = this.toString().match(/function\s*([^(]*)\(/)[1];

}

var o = Object.create(Array);

// Function

console.log(type(o));

JavaScript中的面向對象技術

  1. 一個集合類
function Set() {

  this.values = {};

  this.n = 0;

  this.add.apply(this, arguments);

}

Set.prototype.add = function() {

  for (var i = 0; i < arguments.length; i++) {

    var val = arguments[i];

    var str = Set._v2s(val);

    if (!this.values.hasOwnProperty(str)) {

      this.values[str] = val;

      this.n++;

    }

  }

  return this;

};

Set.prototype.remove = function() {

  for (var i = 0; i < arguments.length; i++) {

    var str = Set._v2s(arguments[i]);

    if (this.values.hasOwnProperty(str)) {

      delete this.values[str];

      this.n--;

    }

  }

  return this;

};

Set.prototype.contains = function(value) {

  return this.values.hasOwnProperty(Set._v2s(value));

};

Set.prototype.size = function() {

  return this.n;

};

Set.prototype.foreach = function(f, context) {

  for (var s in this.values) {

    if (this.values.hasOwnProperty(s)) {

      f.call(context, this.values[s]);

    }

  }

};

Set.prototype.toString = function() {

  var _arr = [];

  for (var k in this.values) {

  _arr.push(this.values[k]);

  }

  console.log('' + _arr);

}

Set._v2s = function(val) {

  switch (val) {

    case undefined: return 'u';

    case null: return 'n';

    case true: return 't';

    case false: return 'f';

    default: switch (typeof val) {

      case 'number': return '#' + val;

      case 'string': return '"' + val;

      default: return '@' + objectId(val);

    }

  }

  function objectId(o) {

    var prop = "|**objectid**|";

    if (!o.hasOwnProperty(prop)) {

      o[prop] = Set._v2s.next++;

    }

    return o[prop];

  }

};

Set._v2s.next = 100;

 

var set = new Set(1, 2, 3, 2, 1);

// 1,2,3

set.toString();

set.add(3, 4, 5);

// 1,2,3,4,5

set.toString();

set.remove(1, 2);

// 3,4,5

set.toString();

// true

console.log(set.contains(4));
  1. 枚舉類型
function enumeration(namesToValues) {

  var enumeration = function() { throw "can't Instantiate Enumerations"; };

 

  var proto = enumeration.prototype = {

    constructor: enumeration,

    toString: function() { return this.name; },

    valueOf: function() { return this.value; },

    toJSON: function() { return this.name; }

  };

 

  enumeration.values = [];

  for (var name in namesToValues) {

    var e = Object.create(proto);

    e.name = name;

    e.value = namesToValues[name];

    enumeration[name] = e;

    enumeration.values.push(e);

  }

 

  enumeration.foreach = function(f, c) {

    for (var i = 0; i < this.values.length; i++) f.call(c, this.values[i]);

  }

 

  return enumeration;

}

 

var Coin = enumeration({Penny: 1, Nickel: 5, Dime: 10, Quarter: 25});

var c = Coin.Dime;

// true

console.log(c instanceof Coin);

// true

console.log(c.constructor == Coin);

// 40

console.log(Coin.Quarter + 3 * Coin.Nickel);

// true

console.log(Coin.Dime == 10);

// true

console.log(Coin.Dime > Coin.Nickel);

// Dime:10

console.log(String(Coin.Dime) + ":" + Coin.Dime);

1) 之因此要在開頭編寫:

var enumeration = function() { ... }

是由於防止以下的調用:

// "can't Instantiate Enumerations

Coin();

自己, enumeration爲一個類型, 而非一個函數.

2) 在enumeration中的每個元素均爲proto的繼承類型, 在proto中還定義了toString()/valueOf()/toJSON的方法. 例如對proto進行計算時候, 如:

Coin.Quarter + 3 * Coin.Nickel

自己調用的是proto的valueOf()方法, 而調用:

String(Coin.Dime)

自己調用的是proto的toString()方法.

  1. 標準轉換方法

toString(): 返回一個能夠表示這個對象的字符串. 在但願用到字符串的地方用到對象的話, JavaScript會自動調用這個方法.

valueOf(): 將對象轉換爲原始值, 例如進行數學運算符/關係運算符做用於數字文本表示的對象時候, 則自動調用這個方法.

toJSON(): 調用JSON.stringify()時候自動調用.

  1. 私有狀態

若是想要變量爲私有, 則能夠運用閉包特性(在實際的項目中, 不多使用)

function Range(from, to) {

  this.from = function() { return from; };

  this.to = function() { return to; };

}

子類

通常使用Object.create()來建立子類(ECMA5中定義的方法).

var super_o = {

  _x: undefined,

  _y: undefined,

  add: function() {

  /* ... */

  }

};

var sub_1 = Object.create(super_o);

var sub_2 = Object.create(super_o);

sub_1.sub = function() {

  /* ... */

};

sub_2.mul = function() {

  /* ... */

};

這裏, super_o自己爲一個prototype原型, 它提供了共享的add方法和_x/_y屬性. 但通常狀況下數據屬性不該該被共享, 而應該綁定到具體的實例中, 因此可修改以下:

var super_o = {

  add: function() {

  return this._x + this._y;

  }

};

var sub_1 = Object.create(super_o);

sub_1._x = 1;

sub_1._y = 2;

// 3

console.log(sub_1.add());

或者如教科書般的寫法:

function Super(x, y) {

  this._x = x;

  this._y = y;

}

Super.prototype = {

  add: function() {

  return this._x + this._y;

  }

};

var sub_1 = new Super(1, 2);

console.log(sub_1.add());

ECMAScript5中的類

  1. 經過defineProperty()來定義對象不可枚舉:
var arr = [1, 2, 3];

arr.show = function() {

  console.log('' + this);

};

// 0 1 2 show

for (var k in arr) {

  console.log(k);

}

 

Object.defineProperty(arr, "show", {

  writable: true, // 可修改

  enumerable: false, // 不可枚舉

  configurable: true, // 可刪除

});

arr.show = 11;

// 11

console.log(arr.show);

// 0 1 2

for (var k in arr) {

  console.log(k);

}
  1. 定義get/set來封裝對象狀態
var o = {

  _x: undefined,

  get x() {

  return this._x;

  },

  set x(value) {

  this._x = value;

  }

};

o.x = 123;

// 123

console.log(o.x);

 

Object.defineProperty(o, "y", {

  get: function() { return this._y; },

  set: function(value) { this._y = value; }

});

o.y = 321;

// 321

console.log(o.y);
  1. 使用Object.preventExtensions()將對象設置爲不可擴展, 也就是不能給對象添加任何新屬性; Object.seal()不只不能添加新屬性, 並且已有的屬性不可配置.
相關文章
相關標籤/搜索