1. 程式人生 > >javascript的執行函式的四種方式

javascript的執行函式的四種方式

該篇文章首發於我的個人部落格:http://cherryblog.site/ 使用github+coding+hexo搭建的靜態部落格,更多文章請移步至我的個人部落格

javascript的函式呼叫和建構函式呼叫

函式呼叫、方法呼叫以及建構函式呼叫

1 函式呼叫

Function絕對是JavaScript中的重中之重。在JavaScript中,Function承擔了procedures, methods, constructors甚至是classes以及modules的功能。
在面向物件程式設計中,functions,methods以及class constructor往往是三件不同的事情,由不同的語法來實現。但是在JavaScript中,這三個概念都由function來實現,通過三種不同的模式。
最簡單的使用模式就是function 呼叫:

function hello(username) { 
  return "hello, " + username; 
} 
hello("Keyser Söze"); // "hello, Keyser Söze" 

2 方法呼叫

而methods這一概念在JavaScript中的表現就是,一個物件的屬性是一個function:同樣的是函式,將其賦值給一個物件的成員以後,就不一樣了。將函式賦值給物件的成員後,那麼這個就不在稱為函式,而應該叫做方法。

var obj = { 
  hello: function() { 
    return "hello, " + this
.username; }, username: "Hans Gruber" }; obj.hello(); // "hello, Hans Gruber"

真正的行為是,呼叫本身才會決定this會繫結到哪個物件,即:
obj1.hello()會將this繫結到obj1,obj2.hello()則會將this繫結到obj2。記住一句話,誰呼叫,this就指向誰
正因為this繫結的這種規則,在下面的用法也是可行的:

function hello() { 
  return "hello, " + this.username; 
} 

var obj1 = { 
  hello: hello, 
  username: "Gordon Gekko"
}; obj1.hello(); // "hello, Gordon Gekko" var obj2 = { hello: hello, username: "Biff Tannen" };_ obj2.hello(); // "hello, Biff Tannen"

但是,在一個普通的函式中,如上面的hello函式,使用this關鍵字是不太好的方式,當它被直接呼叫的時候,this的指向就成了問題。在這種情況下,this往往被指向全域性物件(GlobalObject),在瀏覽器上一般就是window物件。
而這種行為是不確定和沒有意義的。
所以在ES5標準中,如果使用了strict mode,那麼this會被設定為undefined:

function hello() { 
  "use strict"; 
  return "hello, " + this.username; 
} 
hello(); // error: cannot read property "username" of undefined 

以上這種做法是為了讓潛在的錯誤更快的暴露出來,避免了誤操作和難以找到的bug。
區別普通函式呼叫和方法呼叫,直接看這個例子就明確了。

var func = function() {
  alert(this);
};
var o = {};
o.fn = func;
// 比較
alert(o.fn === func);//true
// 呼叫
func();//[object Window]
o.fn();//[object Object]

這裡的執行結果是,兩個函式是相同的,因此列印結果是 true。但是由於兩個函式的呼叫是不一樣的,func 的呼叫,列印的是 [object Window],而o.fn 的列印結果是 [object Object]。
這裡便是函式呼叫與方法呼叫的區別,函式呼叫中,this 專指全域性物件 window,而在方法中 this 專指當前物件,即 o.fn 中的 this 指的就是物件o。

3 建構函式

function的第三種使用模式就是講它作為constructor:
構造器中的this
我們需要分析建立物件的過程,方能知道this的意義. 如下面程式碼:

var Person = function() {
 this.name = "小平果";
};
var p = new Person();

這裡首先定義了函式Person,下面分析一下整個執行:
程式在執行到這一句的時候,不會執行函式體,因此 JavaScript的直譯器並不知道這個函式的內容.
接下來執行new關鍵字,建立物件,直譯器開闢記憶體,得到物件的引用,將新物件的引用交給函式.
緊接著執行函式,將傳過來的物件引用交給this. 也就是說,在構造方法中,this就是剛剛被new創建出來的物件.
然後為this 新增成員,也就是為物件新增成員.
最後函式結束,返回this,將this交給左邊的變數.
分析過建構函式的執行以後,可以得到,建構函式中的this就是當前物件.
構造器中的return
在建構函式中return的意義發生了變化,首先如果在建構函式中,如果返回的是一個物件,那麼就保留原意. 如果返回的是非物件,比如數字、布林和字串,那麼就返回this,如果沒有return語句,那麼也返回this. 看下面程式碼:

// 返回一個物件的 return
var ctr = function() {
 this.name = "趙曉虎";
 return {
 name:"牛亮亮"
 };
};
// 建立物件
var p = new ctr();
// 訪問name屬性
alert(p.name);

//執行程式碼,這裡列印的結果是”牛亮亮”. 因為構造方法中返回的是一個物件,那麼保留return的意義,返回內容為return後面的物件. 再看下面程式碼:

// 定義返回非物件資料的構造器
var ctr = function() {
 this.name = "趙曉虎";
 return "牛亮亮";
};
// 建立物件
var p = new ctr();
// 使用
alert(p);
alert(p.name);

程式碼執行結果是,先彈窗列印[object Object],然後列印”趙曉虎”. 因為這裡 return 的是一個字串,屬於基本型別,那麼這裡的return語句無效,返回的是this物件. 因此第一個列印的是[object Object]而第二個不會列印undefined.

function User(name, passwordHash) { 
  this.name = name; 
  this.passwordHash = passwordHash; 
} 
var u = new User("sfalken", 
  "0ef33ae791068ec64b502d6cb0191387"); 
u.name; // "sfalken" 

使用new關鍵將function作為constructor進行呼叫。和function以及method呼叫不一樣的是,constructor會傳入一個新的物件並將它繫結到this,然後返回該物件作為constructor的返回值。而constructor function本身的作用就是為了初始化該物件。
建構函式呼叫常犯的一個錯誤
興致勃勃地定義了下面這麼個建構函式:

var Coder = function( nick ){ 
this.nick = nick; 
}; 

定義建構函式結束後呢?沒錯,趕緊例項化:

var coder = Coder( 'casper' );

這個coder兄弟叫什麼名字?趕緊列印下:

console.log( coder.nick ); //undefined 
= =b 竟然是undefined!!再回過頭看看例項化的那個語句,不難發現問題出在哪裡:少了個new
var coder = Coder( 'casper' ); //當作普通的函式來呼叫,故內部的this指標其實指向window物件 
console.log( window.nick); //輸出:casper 
var coder = new Coder( 'casper' ); //加上new,一切皆不同,this正確地指向了當前建立的例項 
console.log( coder.nick ); //輸出:casper

這樣的錯誤貌似挺低階的,但出現的概率挺高的,腫麼去避免或減少這種情況的發生呢?
可以在內部實現裡面動下手腳:

var Coder = function( nick ){ 
  if( !(this instanceof Coder) ){ 
    return new Coder( nick ); 
  } 
    this.nick = nick; 
}; 

其實很簡單,例項化的時候,內部判斷下,當前this指向的物件的型別即可,如果非當前建構函式的型別,強制重新呼叫一遍建構函式。
突然覺得Coder這名字不夠洋氣?想用Hacker,好吧,我改。。。數了下,一共有三處要改,這不科學,有沒有辦法只把建構函式的名字改了就行?
當然有:

var Coder = function( nick ){ 
  if( !(this instanceof arguments.callee) ){ 
    return new arguments.callee( nick ); 
  } 
  this.nick = nick; 
}; 

4 間接呼叫

區分apply,call就一句話,

foo.call(this, arg1,arg2,arg3) == foo.apply(this, arguments)==this.foo(arg1, arg2, arg3)
1
例如:
A, B類都有一個message屬性(面向物件中所說的成員),
A有獲取訊息的getMessage方法,
B有設定訊息的setMessage方法,

var b = new B();
//給物件a動態指派b的setMessage方法,注意,a本身是沒有這方法的!
b.setMessage.call(a, "a的訊息");
//下面將顯示"a的訊息"
alert(a.getMessage());
//給物件b動態指派a的getMessage方法,注意,b本身也是沒有這方法的!
alert(b.setMessage());

這就是動態語言 JavaScript call的威力所在!

簡直是”無中生有”,物件的方法可以任意指派,而物件本身一直都是沒有這方法的,注意是指派,通俗點就是,方法是借給另一個物件的呼叫去完成任務,原理上是方法執行時上下文物件改變了.

所以 b.setMessage.call(a, “a的訊息”); 就等於用a作執行時上下文物件呼叫b物件的setMessage方法,而這過程中與b一點關係都沒有, 作用等效於a.setMessage( “a的訊息”);

call, apply作用就是借用別人的方法來呼叫,就像呼叫自己的一樣.

好,理解了call, apply相同處—–作用後,再來看看它們的區別,看過上面例子,相信您大概知道了.

從 b.setMessage.call(a, “a的訊息”) 等效於 a.setMessage( “a的訊息”) 可以看出, “a的訊息”在call中作為一個引數傳遞,

call, apply方法區別是,從第二個引數起, call方法引數將依次傳遞給借用的方法作引數, 而apply直接將這些引數放到一個數組中再傳遞, 最後借用方法的引數列表是一樣的.當引數明確時可用call, 當引數不明確時可用apply給合arguments

相關推薦

javascript執行函式方式

該篇文章首發於我的個人部落格:http://cherryblog.site/ 使用github+coding+hexo搭建的靜態部落格,更多文章請移步至我的個人部落格 javascript的函式呼叫和建構函式呼叫 函式呼叫、方法呼叫以及建構函式呼叫

python執行系統命令的方式

lib 信息 數值 成功 星期 控制 src 圖片 pos 一、os模塊 1. os.system(‘cmd‘) 在子終端運行系統命令,不能獲取命令執行後的返回信息以及執行返回的狀態 import os os.system(‘date‘) # 2016年 06月 30

Python進階(二十六)-多執行緒實現同步的方式

分享一下我的偶像大神的人工智慧教程!http://blog.csdn.net/jiangjunshow 也歡迎轉載我的文章,轉載請註明出處 https://blog.csdn.net/mm2zzyzzp Python進階(二十六)-多執行緒實現同步的四種方式

實現執行緒的方法的方式

Java多執行緒實現的方式有四種 1.繼承Thread類,重寫run方法 2.實現Runnable介面,重寫run方法,實現Runnable介面的實現類的例項物件作為Thread建構函式的target 3.通過Callable和FutureTask建立執行緒 4.通過執行緒池建立執行緒

Java併發程式設計(二)多執行實現方式

Java實現多執行緒的方式 Java實現多執行緒的方式有4種: 繼承Thread方法、實現Runnable介面、實現Callable介面並通過FutureTask建立執行緒、使用ExecutorService。 其中,前兩種執行緒執行結果沒有返回值,後兩種是有返回值的。 1、繼承Th

Javascript繫結click事件的方式總結

一:HTML中新增onclick <button id="vv" onclick="myfunction()" >哈哈</button> 二:JS中定義函式繫結事件! var funcc = function () {

執行緒實現的方式

實現執行緒有四種方式 分別是: 方式一:繼承Thread類,重寫run方法 方式二:實現Runnable介面,實現run方法 方式三:實現Callnable介面,實現call方法 方式四:利用ExecutorService執行緒池的方式建立執行緒

執行shell指令碼的方式(轉)

原文網址:https://www.jb51.net/article/53924.htm 這篇文章主要介紹了Linux中執行shell指令碼的4種方法,即總結在Linux中執行shell指令碼的4種方法。 前提:bash shell 指令碼的方法有多種,現在作個小結。假設我們編寫好的shell指令碼的檔名為

JAVA多執行緒實現的方式

         昨天自己用ExecutorService建立執行緒池做穿透測試了一下,感覺挺有意思,所以又好好的看了一下執行緒的問題,在此轉載了一篇博友的文章,感覺總結的不錯,所以分享一下. Java多執行緒實現方式主要有四種: 繼承Thread類、實現Runnable

mongodb shell 執行js指令碼的方式

1. 互動式 mongo shell大部分的 mongodb 教程,在第一章都會講解這種方式。mongo 127.0.0.1:27017use testdb.users.findOne()2. mongo --eval 執行一段指令碼不進入互動模式,直接在 OS 的命令列下執

spring在web容器啟動時執行初始化方法(方式

需求:在tomcat啟動時開啟一個定時任務,或初始化slor索引 想法:容器啟動時執行方法,最容易想到的就是servlet中可以配置load-on-startup,設定一個正整數也就可以隨容器一起啟動。 問題:上面的方法很好,但是由於定時任務需要去操作資料庫,而專案

C# 中關閉當前執行緒的方式

從查MSDN和從網上查資料可以知道,Dispose()方法,雖然能釋放當前窗體的資源,卻不能強制結束迴圈, 要想強制突出當前程式要用:System.Environment.Exit(int exitcode)方法;該方法:終止當前程序併為基礎作業系統提供指定的退出程式碼。 如下則問題解決: private v

python3.4多執行緒實現同步的方式

臨界資源即那些一次只能被一個執行緒訪問的資源,典型例子就是印表機,它一次只能被一個程式用來執行列印功能,因為不能多個執行緒同時操作,而訪問這部分資源的程式碼通常稱之為臨界區。 1. 鎖機制 threading的Lock類,用該類的acquire函式進行加鎖,用real

C++執行緒同步的方式(Windows)

為什麼要進行執行緒同步?   在程式中使用多執行緒時,一般很少有多個執行緒能在其生命期內進行完全獨立的操作。更多的情況是一些執行緒進行某些處理操作,而其他的執行緒必須對其處理結果進行了解。正常情況下對這種處理結果的瞭解應當在其處理任務完成後進行。   如果不

Android啟動執行緒的方式

Android開發當中,經常都需要開啟子執行緒來進行一些耗時,或者可能產生阻塞的操作。開啟子執行緒主要分開兩大方式,繼承Thread類,或者是實現runnable類。不多說,看程式碼: 1、繼承Thread類的實現: 首先要寫一個子執行緒類,去繼承Thread類,重寫ru

C# 使用委託實現多執行緒呼叫窗體的方式

1、方法一:使用執行緒      功能描述:在用c#做WinFrom開發的過程中。我們經常需要用到進度條(ProgressBar)用於顯示進度資訊。這時候我們可能就需要用到多執行緒,如果不採用多執行緒控制進度條,視窗很容易假死(無法適時看到進度資訊)。下面

java執行緒阻塞喚醒的方式

java在多執行緒情況下,經常會使用到執行緒的阻塞與喚醒,這裡就為大家簡單介紹一下以下幾種阻塞/喚醒方式與區別,不做詳細的介紹與程式碼分析 suspend與resume Java廢棄 sus

指標作為函式引數(處理陣列的方式

//陣列名傳遞給指標子引數 #include <stdio.h> #define N 3 float average(float * g); int main() { float grade[N]={60,75,80}; printf("學生的平均成

Java 多執行緒傳值有三方式,以及另類的第方式

現在博主的需求是:有可能在同一個執行緒類執行不一樣的程式。上邊兩個紅框中的cron4j排程器使用的是一個,根據引數不同來執行的。如果我點選後邊的手動執行一次,按照我上邊給出的java程式碼是無法實現的。看下邊的新的程式碼: (adsbygoogle = window.adsbygoo

執行緒同步的方式(一)

併發concurent與並行parallel的區別: 互斥物件 首先我們需要建立CreateMutex一把互斥物件,我們可以指明當前執行緒是否擁有它,互斥物件完全就像一把鑰匙一樣,我們用WaitForSignalObject來等待這把鑰匙,但是這把鑰匙被等到