【簡明教程】JavaScript面向對象編程

隨著HTML5標準的成熟和在移動開發(fā)領域的大規(guī)模使用,JavaScript正成為Web開發(fā)領域最熱門的開發(fā)語言贸弥,而且隨著NodeJS等技術的發(fā)展下硕,JavaScript的應用也從傳統(tǒng)前端開發(fā)領域延伸到了服務器端開發(fā)线梗。但同時需要注意的是,我們項目中的JavaScript代碼規(guī)模也變得越來越大和更加復雜惊搏。這就要求開發(fā)人員能夠編寫高效且可維護的JavaScript代碼贮乳,雖然JavaScript不像Java那樣對面向對象設計有那么好的支持,但我們可以通過在JavaScript中應用這些面向對象的設計模式胀屿,來使我們寫出更優(yōu)秀的JavaScript代碼塘揣。

在這篇教程中,你將學習基于JavaScript的面向對象編程宿崭。其中的代碼示例是基于EcmaScript 5(JavaScript的標準定義)來實現(xiàn)亲铡。

Java與JavaScript的比對

Java ** JavaScript**
靜態(tài)類型 動態(tài)類型
使用類,接口和枚舉來定義類型 使用函數(shù)和原型來定義類型
在運行時類型無法改變 類型可以在運行時變更
需要給所有變量聲明類型(強類型校驗) 聲明變量時不需要指定類型 (弱類型校驗)
構造器是特殊的方法 構造器也是一個函數(shù),與其他函數(shù)沒有區(qū)別
類和對象是不同的實體 包括構造器奖蔓,函數(shù)原型在內的一切都是對象
支持靜態(tài)方法和實例 不直接支持靜態(tài)方法和實例
通過抽象類和接口支持抽象類型 不直接支持抽象類型
通過private,package,protected,public定義對象的作用域 只支持public成員
提供豐富的繼承機制 通過原型實現(xiàn)繼承
支持方法級的重寫和重載機制 不直接支持重寫和重載
提供豐富的反射特性 具有一些反射特性
通過包提供模塊化支持 沒有直接的模塊化支持

對象類型定義- Object Type

function MyType(){
    if (!(this instanceof MyType))
        throw new Error("Constructor can’t be called as a function");
}

var myInstance = new MyType();
MyType(); // Error: Constructor can’t be called as a function

在Eclipse的JavaScript視圖中赞草,構造器,實例成員吆鹤,靜態(tài)成員和內部函數(shù)都能被識別厨疙,并在Outline視圖中顯示出來。

實例成員 - Instance Members

通過"new"關鍵字可以創(chuàng)建一個實例對象疑务,而實例成員(變量或方法)能夠通過這個實例對象來訪問沾凄。實例成員可以通過"this"關鍵字,原型(prototype)知允,構造器或Object.defineProperty來定義撒蟀。

function Cat(name){
    var voice = "Meow";
    this.name = name;
    this.say = function(){
      return voice;
    }
}
Cat.prototype.eat = function(){
    return "Eating";
}
var cat = new Cat("Fluffy");
Object.defineProperty(cat, "numLegs",{value: 4,writable:true,enumerable:true,configurable:tr
ue});

console.log(cat.name); // Fluffy
console.log(cat.numLegs); // 4
console.log(cat.say()); // Meow
console.log(cat.eat()); // Eating

靜態(tài)成員 - Static Members

JavaScript中并不直接支持靜態(tài)成員。你可以通過構造器來創(chuàng)建靜態(tài)成員温鸽。靜態(tài)成員不允許通過"this"關鍵字直接訪問保屯。

公共靜態(tài)成員

function Factory(){
}

// public static method
Factory.getType = function (){
    return "Object Factory";
};

// public static field
Factory.versionId = "F2.0";
Factory.prototype.test = function(){
    console.log(this.versionId); // undefined
    console.log(Factory.versionId); // F2.0
    console.log(Factory.getType()); // Object Factory
}

var factory = new Factory();
factory.test();

私有靜態(tài)成員

var Book = (function () {
    // private static field
    var numOfBooks = 0;

    // private static method
    function checkIsbn(isbn) {
        if (isbn.length != 10 && isbn.length != 13)
            throw new Error("isbn is not valid!");
    }

    function Book(isbn, title) {
        checkIsbn(isbn);
        this.isbn = isbn;
        this.title = title;
        numOfBooks++;
        this.getNumOfBooks = function () {
            return numOfBooks;
        }
    }

    return Book;
})();

var firstBook = new Book("0-943396-04-2", "First Title");
console.log(firstBook.title); // First Title
console.log(firstBook.getNumOfBooks()); // 1

var secondBook = new Book("0-85131-041-9", "Second Title");
console.log(firstBook.title); // First Title
console.log(secondBook.title); // Second Title
console.log(firstBook.getNumOfBooks()); // 2
console.log(secondBook.getNumOfBooks()); // 2

抽象類型 - Abstract Types

JavaScript是一個弱類型語言,所以當你聲明一個變量時涤垫,不需要指定它的類型姑尺。這就減弱了對于像接口這樣的抽象類型的依賴。但有時候蝠猬,你仍然希望使用抽象類型來將一些共有的功能放在一起切蟋,并采用繼承的機制,讓其他類型也具有相同的功能吱雏,你可以參考下面的示例:

(function(){
    var abstractCreateLock = false;

    // abstract type
    function BaseForm(){
        if(abstractCreateLock)
            throw new Error("Can’t instantiate BaseForm!");
    }
    
    BaseForm.prototype = {};
    BaseForm.prototype.post = function(){
        throw new Error("Not implemented!");
    }

    function GridForm(){
    }

    GridForm.prototype = new BaseForm();
    abstractCreateLock = true;
    GridForm.prototype.post = function(){
        // ...
        return "Grid is posted.";
    }
    
    window.BaseForm = BaseForm;
    window.GridForm = GridForm;
})();

var myGrid = new GridForm();
console.log(myGrid.post()); // Grid is posted.
var myForm = new BaseForm(); // Error: Can’t instantiate BaseForm!

接口 - Interfaces

JavaScript同樣沒有對接口的直接支持敦姻。你可以通過下面代碼中實現(xiàn)的機制來定義接口。

var Interface = function (name, methods) {
    this.name = name;
    // copies array
    this.methods = methods.slice(0);
};

Interface.checkImplements = function (obj, interfaceObj) {
    for (var i = 0; i < interfaceObj.methods.length; i++) {
        var method = interfaceObj.methods[i];
        if (!obj[method] || typeof obj[method] !=="function")
            thrownewError("Interfacenotimplemented! Interface: " + interfaceObj.name + " Method: " + method);
    }
};

var iMaterial = new Interface("IMaterial", ["getName", "getPrice"]);

function Product(name,price,type){
    Interface.checkImplements(this, iMaterial);
    this.name = name;
    this.price = price;
    this.type = type;
}

Product.prototype.getName = function(){
    return this.name;
};
Product.prototype.getPrice = function(){
    return this.price;
};

var firstCar = new Product("Super Car X11",20000,"Car");
console.log(firstCar.getName()); // Super Car X11
delete Product.prototype.getPrice;
var secondCar = new Product("Super Car X12",30000,"Car"); // Error: Interface not implemented!

單例對象 - Singleton Object

如果你希望在全局范圍內只創(chuàng)建一個某一類型的示例歧杏,那么你可以有下面兩種方式來實現(xiàn)一個單例。

var Logger = {
    enabled:true,
    log: function(logText){
      if(!this.enabled)
        return;
      
      if(console && console.log)
        console.log(logText);
      else
        alert(logText);
    }
}

或者

function Logger(){
}
Logger.enabled = true;
Logger.log = function(logText){
    if(!Logger.enabled)
        return;

    if(console && console.log)
        console.log(logText);
    else
        alert(logText);
};
Logger.log("test"); // test
Logger.enabled = false;
Logger.log("test"); //

創(chuàng)建對象 - Object Creation

通過new關鍵字創(chuàng)建

可以使用"new"關鍵字來創(chuàng)建內置類型或用戶自定義類型的實例對象迷守,它會先創(chuàng)建一個空的實例對象犬绒,然后再調用構造函數(shù)來給這個對象的成員變量賦值,從而實現(xiàn)對象的初始化兑凿。

//or var dog = {};
//or var dog = new MyDogType();
var dog = new Object();
dog.name = "Scooby";
dog.owner = {};
dog.owner.name = "Mike";
dog.bark = function(){
   return "Woof";
};

console.log(dog.name); // Scooby
console.log(dog.owner.name); // Mike
console.log(dog.bark()); // Woof

通過字面量直接創(chuàng)建

通過字面量創(chuàng)建對象非常簡單和直接凯力,同時你還可以創(chuàng)建嵌套對象。

var dog = {
  name:"Scooby?",
  owner:{
    name:"Mike"
  },
  bark:function(){
    return "Woof";
  }
};
console.log(dog.name); // Scooby
console.log(dog.owner.name); // Mike
console.log(dog.bark()); // Woof

成員作用域 - Scoping

私有字段 - Private Fields

在JavaScript中沒有對私有字段的直接支持礼华,但你可以通過構造器來實現(xiàn)它咐鹤。首先將變量在構造函數(shù)中定義為私有的,任何需要使用到這個私有字段的方法都需要定義在構造函數(shù)中圣絮,這樣你就可以通過這些共有方法來訪問這個私有變量了祈惶。

function Customer(){
  // private field
  var risk = 0;
  this.getRisk = function(){
    return risk;
  };
  this.setRisk = function(newRisk){
    risk = newRisk;
  };
  this.checkRisk = function(){
    if(risk > 1000)
      return "Risk Warning";
    return "No Risk";
  };
}

Customer.prototype.addOrder = function(orderAmount){
  this.setRisk(orderAmount + this.getRisk());
  return this.getRisk();
};

var customer = new Customer();
console.log(customer.getRisk()); // 0
console.log(customer.addOrder(2000)); // 2000
console.log(customer.checkRisk()); // Risk Warning

私有方法 - Private Methods

私有方法也被稱作內部函數(shù),往往被定義在構造體中,從外部無法直接訪問它們捧请。

function Customer(name){
  var that = this;
  var risk = 0;
  this.name = name;
  this.type = findType();

  // private method
  function findType() {
     console.log(that.name);
     console.log(risk);
     return "GOLD";
   }
}

或者

function Customer(name){
  var that = this;
  var risk = 0;
  this.name = name;

  // private method
  var findType = function() {
     console.log(that.name);
     console.log(risk);
     return "GOLD";
  };
  this.type = findType();
}
var customer = new Customer("ABC Customer"); // ABC Customer
 // 0
console.log(customer.type); // GOLD
console.log(customer.risk); // undefined

如果私有內部函數(shù)被實例化并被構造函數(shù)返回凡涩,那么它將可以從外部被調用。

function Outer(){
  return new Inner();
  
  //private inner
  function Inner(){
     this.sayHello = function(){
        console.log("Hello");
     }
   }
}
(new Outer()).sayHello(); // Hello

特權方法 - Privileged Methods

原型方法中的一切都必須是公共的疹蛉,因此它無法調用類型定義中的私有變量活箕。通過在構造函數(shù)中使用"this."聲明的函數(shù)稱為特權方法,它們能夠訪問私有字段可款,并且可以從外部調用育韩。

function Customer(orderAmount){
  // private field
  var cost = orderAmount / 2;
  this.orderAmount = orderAmount;
  var that = this;
  
  // privileged method
  this.calculateProfit = function(){
    return that.orderAmount - cost;
  };
}

Customer.prototype.report = function(){
  console.log(this.calculateProfit());
};

var customer = new Customer(3000);
customer.report(); // 1500

公共字段 - Public Fields

公共字段能夠被原型或實例對象訪問。原型字段和方法被所有實例對象共享(原型對象本身也是被共享的)闺鲸。當實例對象改變它的某一個字段的值時座慰,并不會改變其他對象中該字段的值,只有直接使用原型對象修改字段翠拣,才會影響到所有實例對象中該字段的值版仔。

function Customer(name,orderAmount){
  // public fields
  this.name = name;
  this.orderAmount = orderAmount;
}

Customer.prototype.type = "NORMAL";
Customer.prototype.report = function(){
  console.log(this.name);
  console.log(this.orderAmount);
  console.log(this.type);
  console.log(this.country);
};

Customer.prototype.promoteType = function(){
  this.type = "SILVER";
};

var customer1 = new Customer("Customer 1",10);
// public field
customer1.country = "A Country";
customer1.report(); // Customer 1
                     // 10
                     // NORMAL
                     // A Country

var customer2 = new Customer("Customer 2",20);
customer2.promoteType();
console.log(customer2.type); // SILVER
console.log(customer1.type); // NORMAL

公共方法 - Public Methods

原型方法是公共的,所有與之關聯(lián)的對象或方法也都是公共的误墓。

function Customer(){
  // public method
  this.shipOrder = function(shipAmount){
     return shipAmount;
  };
}

// public method
Customer.prototype.addOrder = function (orderAmount) {
    var totalOrder = 0;
    for(var i = 0; i < arguments.length; i++) {
      totalOrder += arguments[i];
    }
    return totalOrder;
  };

var customer = new Customer();
// public method
customer.findType = function(){
   return "NORMAL";
};

console.log(customer.addOrder(25,75)); // 100
console.log(customer.shipOrder(50)); // 50
console.log(customer.findType()); // NORMAL

繼承 - Inheritance

有幾種方法可以在JavaScript中實現(xiàn)繼承蛮粮。其中"原型繼承"——使用原型機制實現(xiàn)繼承的方法,是最常用的谜慌。如下面示例:

function Parent(){
  var parentPrivate = "parent private data";
  var that = this;
  
  this.parentMethodForPrivate = function(){
     return parentPrivate;
  };

  console.log("parent");
}

Parent.prototype = {
  parentData: "parent data",
  parentMethod: function(arg){
    return "parent method";
  },
  overrideMethod: function(arg){
    return arg + " overriden parent method";
  }
}

function Child(){
  // super constructor is not called, we have to invoke it
  Parent.call(this);
  console.log(this.parentData);
  var that = this;
  this.parentPrivate = function(){
     return that.parentMethodForPrivate();
  };
  console.log("child");
}

//inheritance
Child.prototype = new Parent();// parent
Child.prototype.constructor = Child;

//lets add extented functions
Child.prototype.extensionMethod = function(){
  return "child’s " + this.parentData;
};

//override inherited functions
Child.prototype.overrideMethod = function(){
  //parent’s method is called
  return "Invoking from child" + Parent.prototype.
  overrideMethod.call(this, " test");
};

var child = new Child();// parent
// parent data
 // child
console.log(child.extensionMethod()); //child’s parent data
console.log(child.parentData); //parent data
console.log(child.parentMethod()); //parent method
console.log(child.overrideMethod()); //Invoking from child test
overriden parent method
console.log(child.parentPrivate()); // parent private data
console.log(child instanceof Parent); //true
console.log(child instanceof Child); //true

當一個成員字段或函數(shù)被訪問時然想,會首先搜索這個對象自身的成員。如果沒有找到欣范,那么會搜索這個對象對應的原型對象变泄。如果在原型對象中仍然沒有找到,那么會在它的父對象中查找成員和原型恼琼。這個繼承關系也被成為 "原型鏈"妨蛹。下面這張圖就反映了原型鏈的繼承關系。

模塊化 - Modularization

當我們的項目中晴竞,自定義的對象類型越來越多時蛙卤,我們需要更有效地組織和管理這些類定義,并控制他們的可見性噩死,相互依賴關系以及加載順序颤难。"命名空間"和"模塊"能夠幫助我們很好地解決這個問題。(EcmaScript 6已經實現(xiàn)了模塊系統(tǒng)已维,但因它還沒有被所有瀏覽器實現(xiàn)行嗤,此處我們仍以ES5為例來進行說明)

命名空間 - Namespaces

JavaScript中并沒有命名空間的概念。我們需要通過對象來創(chuàng)建命名空間垛耳,并將我們定義的對象類型放入其中栅屏。

//create namespace
var myLib = {};
myLib.myPackage = {};
//Register types to namespace
myLib.myPackage.MyType1 = MyType1;
myLib.myPackage.MyType2 = MyType2;

模塊 - Modules

模塊被用來將我們的JavaScript代碼分解到包中飘千。模塊可以引用其他模塊或將自己定義的對象類型對外暴露,以供其他模塊使用既琴。同時它能夠用來管理模塊間的依賴關系占婉,并按照我們指定的順序進行加載。目前有一些第三方庫可以用來實現(xiàn)模塊的管理甫恩。

下面的例子中逆济,我們在模塊里定義新的類型,并且引用其他模塊并將自身的公共類型對外暴露磺箕。

Module.define("module1.js",
               ["dependent_module1.js","dependent_module2.js",...],
               function(dependentMod1, dependentMod2) {//IMPORTS

  //TYPE DEFINITIONS
  function ExportedType1(){
    // use of dependent module’s types
    var dependentType = new dependentMod1.DependentType1();
    ...
  }

  function ExportedType2(){
  }

  ...
  // EXPORTS
  return { ExportedType1: ExportedType1, ExportedType2:ExportedType2,...};
});

//To use a module (can work asynchronously or synchronously):
Module.use(["module1.js"], function(aModule){
  console.log("Loaded aModule!");
  var AType = aModule.AnExportedType;
  var atype1Instance = new AType();
});

自定義異常 - Custom Exceptions

JavaScript中有一些內部定義的異常奖慌,如Error、TypeError和SyntaxError松靡。它們會在運行時被創(chuàng)建和拋出简僧。所有的異常都是"unchecked"。一個普通的對象也可以被用作一個異常雕欺,并在throw語句中拋出岛马。因此,我們可以創(chuàng)建自己定義的異常對象屠列,并且在程序中捕獲它們進行處理啦逆。一個異常處理的最佳實踐是,擴展JavaScript中標準的Error對象笛洛。

function BaseException() {}
BaseException.prototype = new Error();
BaseException.prototype.constructor = BaseException;
BaseException.prototype.toString = function() {
  // note that name and message are properties of Error
  return this.name + ":"+this.message;
};

function NegativeNumberException(value) {
  this.name = "NegativeNumberException";
  this.message = "Negative number!Value: "+value;
}
NegativeNumberException.prototype = new BaseException();
NegativeNumberException.prototype.constructor = NegativeNumberException;

function EmptyInputException() {
  this.name = "EmptyInputException";
  this.message = "Empty input!";
}
EmptyInputException.prototype = new BaseException();
EmptyInputException.prototype.constructor = EmptyInputException;


var InputValidator = (function() {
  var InputValidator = {};
  InputValidator.validate = function(data) {
    var validations = [validateNotNegative, validateNotEmpty];
    for (var i = 0; i < validations.length; i++) {
      try {
        validations[i](data);
      } catch (e) {
        if (e instanceof NegativeNumberException) {
          //re-throw
          throw e;
        } else if (e instanceof EmptyInputException) {
          // tolerate it
          data = "0";
        }
      }
    }
  };
  return InputValidator;

  function validateNotNegative(data) {
    if (data < 0)
      throw new NegativeNumberException(data)
  }

  function validateNotEmpty(data) {
    if (data == "" || data.trim() == "")
      throw new EmptyInputException();
  }
})();

try {
  InputValidator.validate("-1");
} catch (e) {
  console.log(e.toString()); // NegativeNumberException:Negative number!Value: -1
  console.log("Validation is done."); // Validation is done.
}

自定義事件 - Custom Events

自定義事件能夠幫助我們減小代碼的復雜度夏志,并且有效地進行對象之間的解耦。下面是一個典型的自定義事件應用模式:

function EventManager() {}
var listeners = {};

EventManager.fireEvent = function(eventName, eventProperties) {
  if (!listeners[eventName])
    return;
  for (var i = 0; i < listeners[eventName].length; i++) {
    listeners[eventName][i](eventProperties);
  }
};

EventManager.addListener = function(eventName, callback) {
  if (!listeners[eventName])
    listeners[eventName] = [];
  listeners[eventName].push(callback);
};

EventManager.removeListener = function(eventName, callback) {
  if (!listeners[eventName])
    return;
  for (var i = 0; i < listeners[eventName].length; i++) {
    if (listeners[eventName][i] == callback) {
      delete listeners[eventName][i];
      return;
    }
  }
};

EventManager.addListener("popupSelected", function(props) {
  console.log("Invoked popupSelected event: "+props.itemID);
});
EventManager.fireEvent("popupSelected", {
  itemID: "100"
}); //Invoked popupSelected event: 100

編寫組件 - Components

JavaScriipt允許開發(fā)人員通過編寫組件來向HTML元素添加行為苛让。下面是一個典型的應用場景沟蔑,將JavaScript對象綁定到了DOM元素上。

1-定義JavaScript組件

function InputTextNumberComponent(domElement) {
  this.initialize(domElement);
}

InputTextNumberComponent.prototype.initialize =
  function(domElement) {
    domElement.onchange = function() {
      //just a format
      domElement.value = "-" +domElement.value + " -";
    };
    domElement.jsComponent = this; //Expando property
    this.domElement = domElement;
  };
InputTextNumberComponent.prototype.resetValue = function() {
  this.domElement.value = "";
};

2-定義一個CSS樣式用于與JavaScript組件寫作控制HTML元素

<style type="text/css">
  .inputTextNumber { text-align:right; }
</style>

HTML元素的定義如下

<input type="text" class="inputTextNumber" name="NumberField"
size="10" value="Click me!" onClick="this.jsComponent.
resetValue()">

2-當頁面加載時(或DOM元素都已準備就緒)狱杰,檢測HTML元素并創(chuàng)建JavaScript組件

window.onload = function() {
  var inputTextNumbers = document.getElementsByClassName("inputTextNumber");
  for (var i = 0; i < inputTextNumbers.length; i++) {
    var myComp = new InputTextNumberComponent(
      inputTextNumbers.item(i));
  }
};

簡書簽約作者:技匠瘦材,以上內容歡迎大家分享到朋友圈/微博等。如需轉載浦旱,請通過簡信聯(lián)系授權宇色。謝謝大家!

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
  • 序言:七十年代末颁湖,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子例隆,更是在濱河造成了極大的恐慌甥捺,老刑警劉巖,帶你破解...
    沈念sama閱讀 216,496評論 6 501
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件镀层,死亡現(xiàn)場離奇詭異镰禾,居然都是意外死亡皿曲,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,407評論 3 392
  • 文/潘曉璐 我一進店門吴侦,熙熙樓的掌柜王于貴愁眉苦臉地迎上來屋休,“玉大人,你說我怎么就攤上這事备韧〗僬粒” “怎么了?”我有些...
    開封第一講書人閱讀 162,632評論 0 353
  • 文/不壞的土叔 我叫張陵织堂,是天一觀的道長叠艳。 經常有香客問我,道長易阳,這世上最難降的妖魔是什么附较? 我笑而不...
    開封第一講書人閱讀 58,180評論 1 292
  • 正文 為了忘掉前任,我火速辦了婚禮潦俺,結果婚禮上拒课,老公的妹妹穿的比我還像新娘。我一直安慰自己事示,他們只是感情好早像,可當我...
    茶點故事閱讀 67,198評論 6 388
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著很魂,像睡著了一般扎酷。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上遏匆,一...
    開封第一講書人閱讀 51,165評論 1 299
  • 那天法挨,我揣著相機與錄音,去河邊找鬼幅聘。 笑死凡纳,一個胖子當著我的面吹牛,可吹牛的內容都是我干的帝蒿。 我是一名探鬼主播荐糜,決...
    沈念sama閱讀 40,052評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼葛超!你這毒婦竟也來了暴氏?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 38,910評論 0 274
  • 序言:老撾萬榮一對情侶失蹤绣张,失蹤者是張志新(化名)和其女友劉穎答渔,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體侥涵,經...
    沈念sama閱讀 45,324評論 1 310
  • 正文 獨居荒郊野嶺守林人離奇死亡沼撕,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 37,542評論 2 332
  • 正文 我和宋清朗相戀三年宋雏,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片务豺。...
    茶點故事閱讀 39,711評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡磨总,死狀恐怖,靈堂內的尸體忽然破棺而出笼沥,到底是詐尸還是另有隱情蚪燕,我是刑警寧澤,帶...
    沈念sama閱讀 35,424評論 5 343
  • 正文 年R本政府宣布敬拓,位于F島的核電站邻薯,受9級特大地震影響,放射性物質發(fā)生泄漏乘凸。R本人自食惡果不足惜厕诡,卻給世界環(huán)境...
    茶點故事閱讀 41,017評論 3 326
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望营勤。 院中可真熱鬧灵嫌,春花似錦、人聲如沸葛作。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,668評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽赂蠢。三九已至绪穆,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間虱岂,已是汗流浹背玖院。 一陣腳步聲響...
    開封第一講書人閱讀 32,823評論 1 269
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留第岖,地道東北人难菌。 一個月前我還...
    沈念sama閱讀 47,722評論 2 368
  • 正文 我出身青樓,卻偏偏與公主長得像蔑滓,于是被迫代替她去往敵國和親郊酒。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 44,611評論 2 353

推薦閱讀更多精彩內容

  • Spring Cloud為開發(fā)人員提供了快速構建分布式系統(tǒng)中一些常見模式的工具(例如配置管理键袱,服務發(fā)現(xiàn)燎窘,斷路器,智...
    卡卡羅2017閱讀 134,651評論 18 139
  • 點擊查看原文 Web SDK 開發(fā)手冊 SDK 概述 網易云信 SDK 為 Web 應用提供一個完善的 IM 系統(tǒng)...
    layjoy閱讀 13,758評論 0 15
  • 萬里云動風墨色 千里冰封萬里 各自無言以對 沉默的螺旋 迎風而止 封冰云中歌 無話堅冰 黎明踏浪...
    my9311閱讀 274評論 2 5
  • 在開發(fā)中我們經常需要在控制臺中打印出一些數(shù)據(jù)蹄咖,以驗證我們代碼的正確性荠耽。一般我們的需求都是會打印出網絡請求的返回結果...
    天天想念閱讀 3,142評論 0 5
  • 從廣義上說铝量,教育應該教什么?用三個關鍵詞概括就是:認識银亲、思考、改變。 教育在認識層面的意義岗仑,是我們最為熟知的意義辈双,...
    蠟筆小新有個妹妹閱讀 542評論 0 1