The Core of JS

  我基本從來不寫工作的事兒倦青。

  因為工作實在沒啥好寫的瓮床,不就是工作唄。

  然後今天打算稍微寫一點产镐,就寫JS吧隘庄。


  我一直相信,所有的編程語言都可以分為兩部分癣亚,表象與內核丑掺。

  什麼是表象?語法就是表象述雾。

  什麼是內核街州?去除表象以後的就是內核。

  很多人學編程語言學的就是語法绰咽,但語法這貨只要記憶力足夠OK菇肃,半天就能搞定嘛地粪。

  我司有一位新人取募,現(xiàn)在已經被開除了,他說過這麼一句名言:

  我會編程啊蟆技,C玩敏、C++、Java质礼、Ruby和Python的For循環(huán)或者If語句我都會的旺聚。

  一旁旁聽的我聽了只能笑笑。

  這貨另一句名言是:這個和我學校裡學的不一樣翱艚丁砰粹!

  大家一起呵呵。

  PS:此人交大畢業(yè)造挽,到我司的筆試聽說是滿分碱璃。

  回到正題。

  語法是表象饭入,不重要——當然了嵌器,內核總會牽扯到語法怎麼實現(xiàn)的,所以並不是說那麼地不重要谐丢。

  關鍵是拋開表象後的東西爽航。

  在我看來蚓让,JS在拋開無聊的語法表象後,真正剩下的東西是這些:


  1讥珍,對象與域

  對象历极,Object。

  域衷佃,Scope执解。

  一切變量要么隸屬於某個對象,要么隸屬於某個域——最外層的域當然就是DOM/BOM了纲酗。

  比如下面這個:

var name = '???';

function classA () {

? ? var tag = '~~~';

? ? this.name = 'test';

? ? this.show = function () {console.log(this.name + tag + name);};

}

var test = new classA();

  這裡衰腌,最外層的對象有兩個,name和test(String也是對象)觅赊。而最外層的Scope是DOM/BOM右蕊,然後是test內的Scope。

  所以以var形式生命的對象都隸屬於其所在的Scope吮螺,而所有以this.xxx形式聲明的對象都隸屬於所屬的對象(比如classA中的this.name饶囚,在實例化後name這個對象就隸屬於test這個類實例)。

  Scope的最大特點鸠补,就是Scope是可以傳遞的萝风,這也就是JS中的callback這麼強大的一個原因:

var name = 'xxx';

body.addEventListener('click', function clickHandler (evt) {

? ? var tag = 'aaa';

? ? $.ajax({...})

? ? .done(function ajaxHandler??(result) {

? ? ? ? console.log('Done', name, tag);

? ? });

});

  在上面的例子中,最外層的scope被傳遞給函數(shù)clickHandler紫岩,或者更應該說是最外層scope的訪問權限被傳遞給了clickHandler的scope规惰,於是你可以在clickHandler內訪問外層定義的變量name。

  同樣的泉蝌,clickHandler的scope和最外層scope被一起傳遞給ajax請求的處理函數(shù)ajaxHandler中歇万,浴室你在這裡面也就可以訪問最外層的name和clickHandler中定義的tag了。

  如果你看過V8(Google給Chrome系用的JS引擎)的源碼勋陪,你就能找到一個Scope對象贪磺,就是用來幹這事的。為V8寫插件或者給NodeJS寫調用V8的插件(這種一般都是C/C++寫的了)時诅愚,一定要記住給予恰當?shù)腟cope寒锚,將一個底層對象放到不同的Scope中往往會得到不同的效果,因為可以訪問的東西不一樣了违孝。

  當然不是只有Scope可以傳遞刹前,Object也可以傳遞,這就牽扯到了JS中的原型鏈等浊,等下再說腮郊。

  對于scope來說,最合理的理解是這樣的:

  一個對象可以隸屬于多個scope筹燕。如果它隸屬于多個scope轧飞,則所有scope必然滿足這樣一種鏈型關系:S1∈...∈Sn衅鹿。也就是說,Sn必然是最大的scope过咬,然后Sn中包含有Sn-1大渤,Sn-1包含Sn-2,以此類推掸绞,知道最小的S1泵三,這里就是某個對象所在的最小Scope。

  而后衔掸,一個對象只能訪問同Scope的變量烫幕,或者說只有同屬一個scope的對象才能相互訪問,這就是Scope內變量的訪問規(guī)則敞映。

  當一個對象隸屬于多個Scope時较曼,它訪問別的對象的順序就是上面的那條鏈,它會先在S1中尋找指定對象是否存在振愿,然后去S2中尋找指定對象是否存在捷犹,一直到找到指定名字的對象位置——所以說對指定名字的對象的搜索是會在鏈條的某處截斷的,而不會每次都遍歷一遍整個鏈條冕末。這也就是說萍歉,包含對象的更小的Scope會“覆蓋”掉更大的Scope中的同名對象。

  JS中的所有一切都是對象档桃,唯獨scope不是枪孩,這是因為這貨是JS引擎提供的“編制外”成員,并不是JS可以直接訪問和操縱的東西——所以胳蛮,我們可以用原型鏈來修改一個對象的父對象的方法销凑,從而實現(xiàn)元編程丛晌,但如果一個對象的方法是用了類定義函數(shù)scope中的“私有”函數(shù)的話仅炊,我們就對它無能為力了,因為無法操縱scope澎蛛。

  JS最“神奇”的功能之一“閉包”也就是利用scope的特性——閉包不能被外界訪問抚垄,這是因為scope內對象不能被外界訪問,而只能被scope內對象訪問谋逻。

  如果我們在Chrome中打開一個頁面呆馁,那么可以在console中查看一個對象所屬的closjure,這是新版本Chrome所提供的功能毁兆。

  Scope不能被用戶在JS中有意識地創(chuàng)建浙滤,而只能在創(chuàng)建某些JS對象的時候“附帶”地獲得。

  比如在上面的例子中气堕,我們創(chuàng)建一個function纺腊,那么就獲得了這個function內的scope畔咧。我們new了一個類實例,也就在這個實力內創(chuàng)建了一個scope——當然揖膜,這兩件事其實是一件事誓沸。

  又由于scope內的對象只能被scope內對象訪問而不能被外部訪問,所以我們就有了JS中的public和private對象的創(chuàng)建方法壹粟,那就是上頭第一段代碼中的classA這個類拜隧。

  這個類被實例化后,就有了一個Object和一個Scope趁仙。在Scope中我們存放了一個對象tag洪添,而在Object內我們存放了兩個對象name和show。外界可以通過test.name來訪問test這個Object的對象雀费,但無法訪問test被創(chuàng)建時所處scope內的對象tag薇组,從而name和show對外界來說是public的,而tag對外界來說是private的坐儿。

  這是scope的一個比較重要的用法律胀。

  我們可以比較一下JS和Ruby中Scope的差異。

  Ruby中的Scope在Class貌矿、Module和Def中都是閉合掉的炭菌,比如:

my_var = "Global"

class ClassTest

? ? my_var = "Inside Class"

? ? name = "Test-Class"

? ? def show

? ? ? ? puts "My Name Is #{name} in #{my_var}"

? ? end

end

test = ClassTest.new

test.show

  這樣,最后會返回錯誤逛漫,因為在show內無法訪問my_var黑低,也無法訪問name。

  這是和JS中最大的不同酌毡。要穿越這個閉合的Scope Gate克握,可以用如何方法:

my_var = "Global"

ClassTestA = Class.new do

? ? name = "Test-Class"

? ? define_method :show do

? ? ? ? puts "My Name Is #{name} in #{my_var}"

? ? end

end

test = ClassTest.new

test.show

  這樣就可以訪問class外定義的my_var變量了。

  可這么做也有缺點枷踏,那就是show這個函數(shù)無法被重載菩暗,一旦被重載,重載的地方將無法訪問name旭蠕,甚至無法訪問my_var停团。

  回到JS中。JS的Scope的傳遞方式前面已經介紹過了掏熬,所以如果我希望在ScopeA里訪問非ScopeA的上層的ScopeB中的變量佑稠,原則上是不可能的,比如下面這個:

var tag = "global";

function show_tag () {

  console.log(tag);

}

function test (callback) {

  var tag = "inside";

  callback();

}

test(show_tag);

  上面的運行結果是global旗芬。如果我們希望show_tag訪問test內的tag舌胶,這個一般是不可能的,但凡事總有例外疮丛,于是就有了這么一種很危險的突破方法:

function test (callback) {

  var tag = "inside";

  eval("var __callback__ = " + callback.toString() + ";__callback__();");

}

  這樣的運行結果就會是我們所期望的inside幔嫂。

  這種方法的原理其實是是用eval來動態(tài)構造函數(shù)并執(zhí)行該函數(shù)漱办。當我們使用eval來動態(tài)構造函數(shù)的時候,因為執(zhí)行的是新的函數(shù)婉烟,所以原函數(shù)的scope完全無效娩井,新函數(shù)的scope完全由構造函數(shù)的scope決定。

  當然似袁,因為使用eval來動態(tài)構造函數(shù)洞辣,所以這個方法本身也會帶來很多風險。


  2昙衅,原型鏈

  先看這么一段東西:

function classA (name) {

? ? this.name = "I'm " + name;

? ? this.show = function () {

? ??? ? console.log('>> ?' + this.name);? ??

? ? }

}

var a = new classA('A'), b = new classB('B');

a.show();

b.show();

console.log(a.__proto__ === b.__proto__);

console.log(a.show === b.show);

console.log(a.__proto__.show)

  我們看到扬霜,a和b的原型(或者說父對象)是相同的,但a和b的show方法是不同的而涉,而且a和b的父對象是沒有show方法的著瓶。

  接下來這么做:

a.__proto__.test = function () {

? ? console.log('Test ? ?' + this.name);

};

a.test();

b.test();

console.log(a.test === b.test);

console.log(b.__proto__.test);

  這么一來,事情就好玩了啼县,我們終于在a和b的原型上添加了一下好玩的東西了材原。

  事實上,new一個類的過程其實是這樣的:

function classA () {

? ? var a = 'A';

? ? var b = 'B';

? ? this.x = 'X';

? ? this.y = 'Y';

}

var testA = new classA();

function classB () {

? ? var a = 'A';

? ? var b = 'B';

? ? var obj = {};

? ? obj.x = 'X';

? ? obj.y = 'Y';

? ? return obj;

}

var testB = classB();

  我們用new classA()創(chuàng)造出來的東西季眷,和用classB()創(chuàng)造出來的東西余蟹,其實是一樣的。

  這樣子刮,回到一開始的問題:為何a.show和b.show是兩個不同的函數(shù)威酒?

  原因很簡單,它們是兩個不同的對象a和b所獨有的函數(shù)挺峡,而不是來自于某個特殊的共同祖先——原型葵孤。

  可是,有的時候a和b的test函數(shù)其實做的是完全相同的事情:將對象的name屬性打印出來橱赠。既然如此尤仍,如果這函數(shù)不是公用的,這就表示我有幾個classA的實例病线,就創(chuàng)建了幾個show函數(shù)吓著,這對內存是極大的浪費,從而為了節(jié)約內存送挑,我們需要這樣:

function classA (name) {

? ? this.name = name;

}

classA.prototype.show = function () {

? ? console.log("My Name Is " + this.name);

};

  這樣就好了。

  其實這個做法等價于:

(new classA()).__proto__.show = function () {

? ? console.log("My Name Is " + this.name)

};

  這就引出了很好玩的關于原型鏈的話題暖眼。

  JS中的對象通過原型鏈來實現(xiàn)“繼承”惕耕,而原型就是上述obj.__proto__這東西。

  通過一個對象的__proto__接口诫肠,我們可以方位這個對象的“原型”司澎。

  要理解原型欺缘,先要理解類和實例。

  從上面的分析可以看出挤安,類可以被理解為構造對象的一個函數(shù)谚殊,因此JS中其實只有對象的概念,沒有類的概念蛤铜,雖然我們可以通過instanceof來判斷一個對象是否是一個“類”的實力嫩絮,但實際上這貨所作的是比較一個對象的constructor屬性所指的函數(shù)是否是這個對象的構造函數(shù)。

  因此围肥,JS中其實沒有類和實例剿干,有的只是構造函數(shù)和對象。

  對象的原型是另一個對象穆刻,幾個對象C1置尔、C2、C3可以共有一個原型對象P氢伟。當一個對象要尋找對象屬性或者方法(而非Scope屬性或者方法)的時候榜轿,會先從自己開始找起,自己沒有的話就去原型那找朵锣,原型也沒有的話就去原型的原型那找差导,以此類推。

  原型只能傳遞或者說提供“鉤”在對象上的屬性和方法猪勇,而不能提供Scope设褐。

  而這一套機制,就被稱為原型鏈泣刹。

  所以說JS的“繼承”和別的OOP的繼承是那么地不同助析,因為原則上說,JS根本沒有類椅您,所以也就談不上繼承了外冀。而原型鏈又在表現(xiàn)上有點類似繼承的關系,所以就讓人以為JS也有“繼承”掀泳。

  回到一開始的問題雪隧,如果我要讓show方法不被創(chuàng)建多次,應該怎么做员舵?方法就是使用構造函數(shù)的prototype來給出方法脑沿。

  考慮上prototype后,其實一個構造函數(shù)所作的是這么一件事:

function classA () {

? ? this.A = 'A';

}

classA.prototype.show = function () {

? ? return this.A;

};


var classB_prototype = {};

classB_prototype.show = function () {

? ? return this.A;

};

function classB () {

? ? var obj = {};

? ? obj.A = 'A';

? ? obj.__proto__ = classB_prototype;

? ? return obj;

}

  這么一來马僻,我們應該就可以看清楚原型和構造函數(shù)到底是干嘛的了庄拇。

  于此相關的,就是對象的this這個屬性了。

  這是JS引擎提供給對象的固有屬性措近,可以認為是一個指針溶弟,決定了指向具體什么對象——到底是指向構造函數(shù)創(chuàng)造出來的對象,還是這個對象的原型瞭郑,還是整條原型鏈上的某個環(huán)節(jié)辜御。

  需要注意的,就是this本身是屬于scope的屈张,this.xxx才是屬于this所指向的對象的擒权,所以在很多時候直接使用this會出各種各樣的幺蛾子——特別是在使用匿名函數(shù)回調等等東東的時候,一個scope切換袜茧,就等著樂呵吧菜拓。

  this的指向是可以被修改的,這就是JS里經车严茫看到的xxx.call方法纳鼎,將一個函數(shù)xxx內的this指向到指定的obj——

function ClassA (name) {

? ? this.name = name;

}

ClassA.prototype.class = "Class A";

ClassA.prototype.show = function () {

? ? console.log("Object " + this.name + " Of Class " + this.class);

};

var objA = new ClassA("Object A");

objA.show();

var objTmp = {

? ? name : "No Name",

? ? class: "No Class"

};

objA.show.call(objTmp);

  運行結果為:

Object Object A Of Class Class A

Object No Name Of Class No Class

  由于在復雜環(huán)境中,Scope的轉移和this的轉移根據(jù)不同的規(guī)則來——scope依賴于上下文context裳凸,而this依賴于原型鏈贱鄙,所以JS才會有很多很豐富的特性——尤其是各種回調和異步方式。

  我們可以ruby做一個對比姨谷。

  在ruby中是的確有類這個概念的逗宁,而且,ruby的動態(tài)特性允許我們修改已經定義好的類的定義——這在js中是做不到的梦湘,我們只能修改一個對象的原型瞎颗,但不能修改這個對象的類定義本身——所以JS的諸多編程模式中有一個就是在所有能用原型的地方都用到了原型,這樣當我要修改類定義的時候捌议,就把原型充當類定義來用了哼拔。這么做有好的地方,當然也有不好的地方——原型鏈過長的話瓣颅,每次用this調用屬性/方法的時候的開銷也就大了倦逐。

  而另一方面,ruby可以通過module和mix-in做到很好的代碼重用和多重繼承宫补,這個在js里比較麻煩檬姥,因為一個js的原型只有一個——當然,這事也不是說就做不到粉怕,只不過麻煩一點:

function clsP1 () {

? ? this.funcitonA = ...

}

function clsP2 () {

? ? this.funcitonB = ....

}

funciton clsPMain () {

? ? this.propertyC = ....

}

function clsChild () {

? ? var me = this;

? ? var p1 = new clsP1(), p2 = new clsP2();

? ? this.functionA = function () {

? ? ? ? p1.functionA.call(me, arguments);

? ? };

? ? this.functionB = function () {

? ? ? ? p2.functionB.call(me, arguments);

? ? };

}

clsChild.prototype = new clsPMain();

  在上面的例子中健民,clsChild的實例的原型是clsPMain的實例,但clsChild的實例依然可以使用clsP1和clsP2的方法(而且this指向的確是它們倆)斋荞。這也是一種很常見的多重繼承方案荞雏。

  與此相關的一個有趣的現(xiàn)象,那就是JS中的對象是可以“換家長”的平酿,而ruby就不能這么“放肆”:

var ParentA = {

  show: function () {

    console.log(">>>>>> " + this.name);

  }

}

var ParentB = {

  show: function () {

    console.log(this.name + " <<<<<<");

  }

}

function classClass (name) {

  this.name = name;

}

classClass.prototype = ParentA;

var obj = new classClass("Lost");

obj.show();

classClass.prototype = ParentB;

obj.show();

obj.__proto__ = ParentB;

obj.show();

obj.__proto__ = ParentA;

obj.show();

  像這種隨便換老爹的事情凤优,大概是JS絕無僅有的吧。蜈彼。筑辨。這也是原型鏈和繼承的最大不同。

  我們事實上可以說幸逆,所謂繼承棍辕,就是類的原型鏈,但和對象的原型鏈畢竟還是不同的还绘。

  Ruby雖然一樣是動態(tài)語言楚昭,但Ruby畢竟還有類的概念,從而也有訪問器的概念拍顷,而這個JS就沒有抚太。所以當我們在看Ruby很歡脫地private、public或者protected的時候昔案,JS的心理總是會有一些酸楚尿贫。當然,世事無絕對踏揣,JS也可以玩訪問器庆亡,不過就是麻煩點:

function isNull (obj) {

  if (obj === null) return true;

  if (obj === undefined) return true;

  if (typeof obj === 'undefined') return true;

  return false;

}

function classMap () {

  var keys = [], values = [];

  this.set = function (key, value) {

    var index = keys.indexOf(key);

    if (index === -1) {

      keys.push(key);

      values.push(value);

    }

    else {

      values[index] = value;

    }

  };

  this.get = function (key) {

    var index = keys.indexOf(key);

    if (index === -1) return null;

    return values[index];

  };

  this.remove = function (key) {

    var index = keys.indexOf(key);

    if (index === -1) return;

    keys.splice(index, 1, 0);

    values.splice(index, 1, 0);

  };

  this.find = function (value) {

    var index = values.indexOf(value);

    if (index === -1) return null;

    return keys[index];

  };

  this.each = function (callback) {

    var l = keys.length, i;

    for (i = 0; i < l; i++) {

      callback(keys[i], values[i]);

    }

  };

}

var jlass = (function jlass (global) {

  var obj_net = new classMap();

  function clsAccessor (host) {

    this.public = function (name, obj) {

      if (isNull(obj)) {

        return getPublic(host, name);

      }

      else {

        return setPublic(host, name, obj);

      }

    }

    this.private = function (name, obj) {

      if (isNull(obj)) {

        return getPrivate(host, name);

      }

      else {

        return setPrivate(host, name, obj);

      }

    }

  }

  function newAccessor (host) {

    var accessor = new clsAccessor(host);

    checkLink(host).accessor = accessor;

    return accessor;

  }

  function getAccessor (host) {

    return checkLink(host).accessor;

  }

  function checkLink (obj) {

    var link = obj_net.get(obj);

    if (isNull(link)) {

      link = {

        parent: [],

        accessor: null,

        vPrivate: {}

      };

      obj_net.set(obj, link);

    }

    return link;

  }

  function setParent (child, parent, isPrototype) {

    var link = checkLink(child);

    if (!isNull(isPrototype) || link.parent.length <= 0) {

      child.__proto__ = parent;

    }

    if (link.parent.indexOf(parent) < 0) {

      link.parent.push(parent);

    }

  }

  function getPublic (host, name) {

    return host[name];

  }

  function getPrivate (host, name) {

    var vars = checkLink(host);

    var result = vars.vPrivate[name];

    if (!isNull(result)) return result;

    var parents = vars.parent, l = parents.length, i;

    for (i = l - 1; i >= 0; i--) {

      result = getPrivate(parents[i], name);

      if (!isNull(result)) return result;

    }

    return null;

  }

  function setPublic (host, name, obj) {

    host[name] = obj;

    return obj;

  }

  function setPrivate (host, name, obj) {

    var vars = checkLink(host);

    vars.vPrivate[name] = obj;

    return obj;

  }

  var jlass = function (className, structure) {

    var constructor = function () {

      var obj = {};

      obj.extend = function (parent) {

        setParent(obj, parent);

      };

      var args = [], l = arguments.length, i;

      newAccessor(obj);

      args.push(getAccessor);

      for (i = 0; i < l; i++) args.push(arguments[i]);

      structure.apply(obj, args);

      return obj;

    };

    constructor.className = className;

    return constructor;

  };

  return jlass;

}) ();

var classTest = new jlass('Class Test', function (accessor, name, age, sex) {

  this.family = "Winter";

  accessor(this).public('name', name);

  accessor(this).private('age', age);

  accessor(this).private('sex', sex);

  this.sayName = function () {

    console.log("Name: " + accessor(this).public('name'));

  };

  this.sayAge = function () {

    console.log("Age : " + accessor(this).private('age'));

  };

  this.saySex = function () {

    console.log("Sex : " + accessor(this).private('sex'));

  };

});

var objTest = new classTest('Test Object', 18, true);

console.log(objTest);

objTest.sayName();

objTest.sayAge();

objTest.saySex();

console.log('XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX');

var classChild = new jlass('Class Child', function (accessor, name, job, age) {

  this.family = "Winter";

  accessor(this).public('name', name);

  accessor(this).private('job', job);

  accessor(this).private('age', age);

  this.sayJob = function () {

    console.log("Job : " + accessor(this).private('job'));

  };

});

var objChild = new classChild("Test Child", "RD", 20);

objChild.extend(objTest);

console.log(objChild);

objChild.sayName();

objChild.sayJob();

objChild.sayAge();

objChild.saySex();

  耶~~


  除此以外的JS相關的部分,個人都認為沒啥特別值得記錄的捞稿,無非就是熟能生巧耳又谋,所以就只寫這兩個東西。


  臨睡前寫的總結娱局,萬一哪里寫錯了的話彰亥,大家?guī)兔σ黄鹱较x吧~~~

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市铃辖,隨后出現(xiàn)的幾起案子剩愧,更是在濱河造成了極大的恐慌,老刑警劉巖娇斩,帶你破解...
    沈念sama閱讀 218,755評論 6 507
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件仁卷,死亡現(xiàn)場離奇詭異,居然都是意外死亡犬第,警方通過查閱死者的電腦和手機锦积,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,305評論 3 395
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來歉嗓,“玉大人丰介,你說我怎么就攤上這事。” “怎么了哮幢?”我有些...
    開封第一講書人閱讀 165,138評論 0 355
  • 文/不壞的土叔 我叫張陵带膀,是天一觀的道長。 經常有香客問我橙垢,道長垛叨,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,791評論 1 295
  • 正文 為了忘掉前任柜某,我火速辦了婚禮嗽元,結果婚禮上,老公的妹妹穿的比我還像新娘喂击。我一直安慰自己剂癌,他們只是感情好,可當我...
    茶點故事閱讀 67,794評論 6 392
  • 文/花漫 我一把揭開白布翰绊。 她就那樣靜靜地躺著佩谷,像睡著了一般。 火紅的嫁衣襯著肌膚如雪辞做。 梳的紋絲不亂的頭發(fā)上琳要,一...
    開封第一講書人閱讀 51,631評論 1 305
  • 那天,我揣著相機與錄音秤茅,去河邊找鬼稚补。 笑死,一個胖子當著我的面吹牛框喳,可吹牛的內容都是我干的课幕。 我是一名探鬼主播,決...
    沈念sama閱讀 40,362評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼五垮,長吁一口氣:“原來是場噩夢啊……” “哼乍惊!你這毒婦竟也來了?” 一聲冷哼從身側響起放仗,我...
    開封第一講書人閱讀 39,264評論 0 276
  • 序言:老撾萬榮一對情侶失蹤润绎,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后诞挨,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體莉撇,經...
    沈念sama閱讀 45,724評論 1 315
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 37,900評論 3 336
  • 正文 我和宋清朗相戀三年惶傻,在試婚紗的時候發(fā)現(xiàn)自己被綠了棍郎。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 40,040評論 1 350
  • 序言:一個原本活蹦亂跳的男人離奇死亡银室,死狀恐怖涂佃,靈堂內的尸體忽然破棺而出励翼,到底是詐尸還是另有隱情,我是刑警寧澤辜荠,帶...
    沈念sama閱讀 35,742評論 5 346
  • 正文 年R本政府宣布汽抚,位于F島的核電站,受9級特大地震影響侨拦,放射性物質發(fā)生泄漏殊橙。R本人自食惡果不足惜辐宾,卻給世界環(huán)境...
    茶點故事閱讀 41,364評論 3 330
  • 文/蒙蒙 一狱从、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧叠纹,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,944評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至持偏,卻和暖如春驼卖,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背鸿秆。 一陣腳步聲響...
    開封第一講書人閱讀 33,060評論 1 270
  • 我被黑心中介騙來泰國打工酌畜, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人卿叽。 一個月前我還...
    沈念sama閱讀 48,247評論 3 371
  • 正文 我出身青樓桥胞,卻偏偏與公主長得像,于是被迫代替她去往敵國和親考婴。 傳聞我的和親對象是個殘疾皇子贩虾,可洞房花燭夜當晚...
    茶點故事閱讀 44,979評論 2 355

推薦閱讀更多精彩內容

  • 工廠模式類似于現(xiàn)實生活中的工廠可以產生大量相似的商品,去做同樣的事情沥阱,實現(xiàn)同樣的效果;這時候需要使用工廠模式缎罢。簡單...
    舟漁行舟閱讀 7,766評論 2 17
  • 單例模式 適用場景:可能會在場景中使用到對象,但只有一個實例考杉,加載時并不主動創(chuàng)建策精,需要時才創(chuàng)建 最常見的單例模式,...
    Obeing閱讀 2,073評論 1 10
  • ECMAScript關鍵字 delete do else finally function in instance...
    doudou2閱讀 721評論 0 0
  • 1.語言基礎2.嚴格模式3.js組成(ECMAScript DOM BOM)4.各種(DOM BOM)例子5.組件...
    蒲公英_前端開發(fā)者閱讀 1,545評論 0 3
  • 在開發(fā)iOS的過程中奔则,我們有時候可能需要自己開發(fā)一些API給別人使用蛮寂,但是又不想暴露具體的實現(xiàn)代碼給別人(或者防止...
    live111閱讀 475評論 0 0