摘要:對象在中,除了數(shù)字字符串布爾值這幾個簡單類型外,其他的都是對象。那么在函數(shù)對象中,這兩個屬性的有什么區(qū)別呢表示該函數(shù)對象的原型表示使用來執(zhí)行該函數(shù)時這種函數(shù)一般成為構(gòu)造函數(shù),后面會講解,新創(chuàng)建的對象的原型。這時的函數(shù)通常稱為構(gòu)造函數(shù)。。
本文原發(fā)于我的個人博客,經(jīng)多次修改后發(fā)到sf上。本文仍在不斷修改中,最新版請訪問個人博客。
最近工作一直在用nodejs做開發(fā),有了nodejs,前端、后端、腳本全都可以用javascript搞定,很是方便。但是javascript的很多語法,比如對象,就和我們常用的面向?qū)ο蟮木幊陶Z言不同;看某個javascript開源項目,也經(jīng)常會看到使用this關(guān)鍵字,而這個this關(guān)鍵字在javascript中因上下文不同而意義不同;還有讓人奇怪的原型鏈。這些零零碎碎的東西加起來就很容易讓人不知所措,所以,有必要對javascript這門語言進行一下深入了解。
我這篇文章主要想說說如何在javascript中進行面向?qū)ο蟮木幊?,同時會講一些javascript這門語言在設(shè)計之初的理念。下面讓我們開始吧。
首先強調(diào)一下,我們現(xiàn)在廣泛使用的javascript都是遵循了ECMAScript 5.1標準的,正在制定中的版本為6.0,這個版本變化很大,增加了很多新的語法與函數(shù),大家可以去Mozilla Developer Network上查看。
設(shè)計理念javascript1.0 最初是由網(wǎng)景公司的Brendan Eich在1995年5月花了十天搞出來的,Eich的目標是設(shè)計出一種即輕量又強大的語言,所以Eich充分借鑒了其他編程語言的特性,比如Java的語法(syntax)、Scheme的函數(shù)(function)、Self的原型繼承(prototypal inheritance)、Perl的正則表達式等。
其中值得一提的是,為什么繼承借鑒了Self語言的原型機制而不是Java的類機制?首先我們要知道:
Self的原型機制是靠運行時的語義
Java的類機制是靠編譯時的類語法
Javascript1.0的功能相對簡單,為了在今后不斷豐富javascript本身功能的同時保持舊代碼的兼容性,javascript通過改變運行時的支持來增加新功能,而不是通過修改javascript的語法,這就保證了舊代碼的兼容性。這也就是javascript選擇基于運行時的原型機制的原因。
wikipedia這樣描述到:JavaScript is classified as a prototype-based scripting language with dynamic typing and first-class functions。這些特性使得javascript是一種多范式的解釋性編程語言,支持面向?qū)ο?、命令?imperative)、函數(shù)式(functional)編程風格。
對象在javascript中,除了數(shù)字、字符串、布爾值(true/false)、undefined這幾個簡單類型外,其他的都是對象。
數(shù)字、字符串、布爾值這些簡單類型都是不可變量,對象是可變的鍵值對的集合(mutable keyed conllections),對象包括數(shù)組Array、正則表達式RegExp、函數(shù)Function,當然對象Object也是對象。
對象在javascript中說白了就是一系列的鍵值對。鍵可以是任何字符串,包括空串;值可以是除了undefined以外的任何值。在javascript中是沒有類的概念(class-free)的,但是它有一個原型鏈(prototype linkage)。javascript對象通過這個鏈來實現(xiàn)繼承關(guān)系。
javascript中有一些預(yù)定義對象,像是Object、Function、Date、Number、String、Array等。
字面量(literal)javascript中的每種類型的對象都可以采用字面量(literal)的方式創(chuàng)建。
對于Object對象,可以使用對象字面量(Object literal)來創(chuàng)建,例如:
var empty_object = {};//創(chuàng)建了一個空對象 //創(chuàng)建了一個有兩個屬性的對象 var stooge = { "first-name": "Jerome", "last-name": "Howard" };
當然,也可以用new Object()或Object.create()的方式來創(chuàng)建對象。
對于Function、Array對象都有其相應(yīng)的字面量形式,后面會講到,這里不再贅述。
原型鏈(prototype linkage)javascript中的每個對象都隱式含有一個[[prototype]]屬性,這是ECMAScript中的記法,目前各大瀏覽器廠商在實現(xiàn)自己的javascript解釋器時,采用的記法是__proto__,也就是說每個對象都隱式包含一個__proto__屬性。舉個例子:
var foo = { x: 10, y: 20 };
foo這個對象在內(nèi)存中的存儲結(jié)構(gòu)大致是這樣的:
當有多個對象時,通過__proto__屬性就能夠形成一條原型鏈。看下面的例子:
var a = { x: 10, calculate: function (z) { return this.x + this.y + z; } }; var b = { y: 20, __proto__: a }; var c = { y: 30, __proto__: a }; // call the inherited method b.calculate(30); // 60 c.calculate(40); // 80
上面的代碼在聲明對象b、c時,指明了它們的原型為對象a(a的原型默認指向Object.prototye,Object.prototype這個對象的原型指向null),這幾個對象在內(nèi)存中的結(jié)構(gòu)大致是這樣的:
這里需要說明一點,我們?nèi)绻朐诼暶鲗ο髸r指定它的原型,一般采用Object.create()方法,這樣效率更高。
除了我們這里說的__proto__屬性,相信大家平常更常見的是prototype屬性。比如,Date對象中沒有加幾天的函數(shù),那么我們可以這么做:
Date.prototype.addDays = function(n) { this.setDate(this.getDate() + n); }
那么以后所有的Date對象都擁有addDays方法了(后面講解繼承是會解釋為什么)。那么__proto__屬性與prototype屬性有什么區(qū)別呢?
javascript的每個對象都有__proto__屬性,但是只有函數(shù)對象有prototype屬性。
那么在函數(shù)對象中, 這兩個屬性的有什么區(qū)別呢?
__proto__表示該函數(shù)對象的原型
prototype表示使用new來執(zhí)行該函數(shù)時(這種函數(shù)一般成為構(gòu)造函數(shù),后面會講解),新創(chuàng)建的對象的原型。例如:
var d = new Date(); d.__proto__ === Date.prototype; //這里為true
看到這里,希望大家能夠理解這兩個屬性的區(qū)別了。
在javascript,原型和函數(shù)是最重要的兩個概念,上面說完了原型,下面說說函數(shù)對象。
函數(shù)對象Function首先,函數(shù)在javascript中無非也是個對象,可以作為value賦值給某個變量,唯一不同的是函數(shù)能夠被執(zhí)行。
使用對象字面量方式創(chuàng)建的對象的__proto__屬性指向Object.prototype(Object.prototype的__proto__屬性指向null);使用函數(shù)字面量創(chuàng)建的對象的__proto__屬性指向Function.prototype(Function.prototype對象的__proto__屬性指向Object.prototype)。
函數(shù)對象除了__proto__這個隱式屬性外,還有兩個隱式的屬性:
函數(shù)的上下文(function’s context)
實現(xiàn)函數(shù)的代碼(the code that implements the function’s behavior)
和對象字面量一樣,我們可以使用函數(shù)字面量(function literal)來創(chuàng)建函數(shù)。類似于下面的方式:
//使用字面量方式創(chuàng)建一個函數(shù),并賦值給add變量 var add = function (a, b) { return a + b; };
一個函數(shù)字面量有四個部分:
function關(guān)鍵字,必選項。
函數(shù)名,可選項。上面的示例中就省略了函數(shù)名。
由圓括號括起來的一系列參數(shù),必選項。
由花括號括起來的一系列語句,必選項。該函數(shù)執(zhí)行時將會執(zhí)行這些語句。
函數(shù)調(diào)用與this一個函數(shù)在被調(diào)用時,除了聲明的參數(shù)外,還會隱式傳遞兩個額外的參數(shù):this與arguments。
this在OOP中很重要,this的值隨著調(diào)用方式的不同而不同。javascript中共有四種調(diào)用方式:
method invocation pattern。當函數(shù)作為某對象一個屬性調(diào)用時,this指向這個對象。this賦值過程發(fā)生在函數(shù)調(diào)用時(也就是運行時),這叫做late binding
function invocation pattern。當函數(shù)不作為屬性調(diào)用時,this指向全局對象,這是個設(shè)計上的錯誤,正確的話,內(nèi)部函數(shù)的this應(yīng)該指向外部函數(shù)??梢酝ㄟ^在函數(shù)中定義一個變量來解決這個問題。
var add = function(a, b) {return a+b;} var obj = { value: 3, double: function() { var self = this;//把this賦值給了self this.value = add(self.value, self.value); } } obj.double(); //obj.value現(xiàn)在為6
construct invocation pattern。javascript是一門原型繼承語言,這也就意味著對象可以直接從其他對象中繼承屬性,沒有類的概念。這和java中的繼承不一樣。但是javascript提供了一種類似與java創(chuàng)建對象的語法。當一個函數(shù)用new來調(diào)用時,this指向新創(chuàng)建的對象。這時的函數(shù)通常稱為構(gòu)造函數(shù)。
apply invocation pattern。使用函數(shù)對象的apply方法來執(zhí)行時,this指向apply的第一個參數(shù)。
除了this外,函數(shù)在調(diào)用是額外傳入的另一個參數(shù)是arguments。它是函數(shù)內(nèi)部的一個變量,包含函數(shù)調(diào)用處的所有參數(shù),甚至包含函數(shù)定義時沒有的參數(shù)。
var sum = function () { var i, sum = 0; for (i = 0; i < arguments.length; i += 1) { sum += arguments[i]; } return sum; }; sum(4, 8, 15, 16, 23, 42); // 108
需要注意的是,這里的arguments不是一個數(shù)組,它只是一個有l(wèi)ength屬性的類數(shù)組對象(Array-like),它并不擁有數(shù)組的其他方法。
關(guān)于對象,最后說一下數(shù)組,javascript中的數(shù)組和平常編程中的數(shù)組不大一樣。
數(shù)組對象Array數(shù)組是一種在內(nèi)存中線性分配的數(shù)據(jù)結(jié)構(gòu),通過下標計算出元素偏移量,從而取出元素。數(shù)組應(yīng)該是一個快速存取的數(shù)據(jù)結(jié)構(gòu),但是在javascript中,數(shù)組不具備這種特性。
數(shù)組在javascript中一個具有傳統(tǒng)數(shù)組特性的對象,這種對象能夠把數(shù)組下標轉(zhuǎn)為字符串,然后把這個字符串作為對象的key,最后對取出對應(yīng)該key的value(這又一次說明了對象在javascript中就是一系列鍵值對)。
雖然javascript中的數(shù)組沒有傳統(tǒng)語言中的數(shù)組那么快,但是由于javascript是弱類型的語言,所以javascript中的數(shù)組可以存放任何值。此外Array有很多實用的方法,大家可以去MDN Array查看。
javascript也為數(shù)組提供了很方便的字面量(Array Literal)定義方式:
var arr = [1,2,3]
通過數(shù)組字面量創(chuàng)建的數(shù)組對象的__proto__指向Array.prototype。
繼承Inheritance在Java中,對象是某個類的實例,一個類可以從另一個類中繼承。但是在基于原型鏈的javascript中,對象可以直接從另一個對象創(chuàng)建。
在上面講解對象時,我們知道了在創(chuàng)建一個對象時,該對象會自動賦予一個__proto__屬性,使用各種類型的字面量(Literal)時,javascript解釋器自動為__proto__進行了賦值。當我們在javascript執(zhí)行使用new操作符創(chuàng)建對象時,javascript解釋器在構(gòu)造函數(shù)時,同時會執(zhí)行類似于下面的語句
this.__proto__ = {constructor: this};
新創(chuàng)建的對象都會有一個__proto__屬性,這個屬性有一個constructor屬性,并且這個屬性指向這個新對象。舉個例子:
var d = new Date() d.__proto__.constructor === Date //這里為true
如果new不是一個操作符,而是一個函數(shù)的話,它的實現(xiàn)類似于下面的代碼:
Function.prototype.new = function () { // Create a new object that inherits from the constructor"s prototype. var that = Object.create(this.prototype); // Invoke the constructor, binding –this- to the new object. var other = this.apply(that, arguments); // If its return value isn"t an object, substitute the new object. return (typeof other === "object" && other) || that; };
之前也說了,基于原型的繼承機制是根據(jù)運行時的語義決定的,這就給我們提供了很大的便利。比如,我們想為所有的Array添加一個map函數(shù),那么我們可以這么做:
Array.prototype.map = function(f) { var newArr = []; for(i=0; i因為所有的數(shù)組對象的__proto__都指向Array.prototype對象,所以我們?yōu)檫@個對象增加方法,那么所有的數(shù)組對象就都擁有了這個方法。
javascript解釋器會順著原型鏈查看某個方法或?qū)傩?。如果想查看某對象的是否有某個屬性,可以使用Object.prototype.hasOwnProperty方法。
總結(jié)通過上面多次講解,希望大家對對象在javascript中就是一系列的鍵值對、原型與函數(shù)這三個概念有更加深刻的認識,使用javascript來寫前端、后端與腳本。在React.js 2015大會上,F(xiàn)acebook公布了即將開源的React Native,這意味著今后我們可以用javascript來寫IOS、Android的原生應(yīng)用了,這可真是learn-once, write-anywhere。相信隨著ECMAScript 6的發(fā)布,javascript這門語言還會有一系列翻天覆地的變化,Stay Tuned。:-)
參考JavaScript. The core
《Javascript: The Good Parts》強烈建議大家去看這個書。
文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請注明本文地址:http://www.ezyhdfw.cn/yun/85531.html
摘要:所支持的面向?qū)ο缶幊贪ㄔ屠^承。發(fā)明于年的就是首批支持函數(shù)式編程的語言之一,而演算則可以說是孕育了這門語言。即使在今天,這個家族的編程語言應(yīng)用范圍依然很廣。 1. 能說出來兩種對于 JavaScript 工程師很重要的編程范式么? JavaScript 是一門多范式(multi-paradigm)的編程語言,它既支持命令式(imperative)/面向過程(procedural)編程...
摘要:所支持的面向?qū)ο缶幊贪ㄔ屠^承。發(fā)明于年的就是首批支持函數(shù)式編程的語言之一,而演算則可以說是孕育了這門語言。即使在今天,這個家族的編程語言應(yīng)用范圍依然很廣。 1. 能說出來兩種對于 JavaScript 工程師很重要的編程范式么? JavaScript 是一門多范式(multi-paradigm)的編程語言,它既支持命令式(imperative)/面向過程(procedural)編程...
摘要:所支持的面向?qū)ο缶幊贪ㄔ屠^承。發(fā)明于年的就是首批支持函數(shù)式編程的語言之一,而演算則可以說是孕育了這門語言。即使在今天,這個家族的編程語言應(yīng)用范圍依然很廣。 1. 能說出來兩種對于 JavaScript 工程師很重要的編程范式么? JavaScript 是一門多范式(multi-paradigm)的編程語言,它既支持命令式(imperative)/面向過程(procedural)編程...
摘要:對象重新認識面向?qū)ο竺嫦驅(qū)ο髲脑O(shè)計模式上看,對象是計算機抽象現(xiàn)實世界的一種方式。除了字面式聲明方式之外,允許通過構(gòu)造器創(chuàng)建對象。每個構(gòu)造器實際上是一個函數(shù)對象該函數(shù)對象含有一個屬性用于實現(xiàn)基于原型的繼承和共享屬性。 title: JS對象(1)重新認識面向?qū)ο? date: 2016-10-05 tags: JavaScript 0x00 面向?qū)ο?從設(shè)計模式上看,對象是...
閱讀 1300·2021-11-25 09:43
閱讀 1395·2021-09-26 09:55
閱讀 2481·2021-09-10 11:20
閱讀 3427·2019-08-30 15:55
閱讀 1524·2019-08-29 13:58
閱讀 1235·2019-08-29 12:36
閱讀 2427·2019-08-29 11:18
閱讀 3489·2019-08-26 11:47