摘要:全局的函數第個對象第個對象作為構造器進行調用也就是利用運算符進行調用。與操作的共同使用只有通過操作產生的對象,可以使用構造器函數原型鏈上的內容,否則對象只能使用自己原型鏈上的內容。
今天這個話題是因為這幾天看了《JavaScript忍者秘籍》,感覺這本書把這幾個內容講的蠻透徹了,特撰本文,以便日后翻閱。(應該都會以知識點的形式給出吧。)
函數 1.【基本類型】JavaScript中函數為first-class object,typeof的結果是object,沒有function這個基本類型,但有可以調用的Function構造器。說到這個就列舉一下JavaScript中的幾個基本類型:
* null (空值) * undefined (未定義) * boolean (布爾) * number (數字) * string (字符串) * object (對象) * symbol (符號)
除了null的typeof結果是object以外,其他類型的typeof都是自己的基本類型名稱。這幾個類型還能有一堆可以講的地方、之后再寫~
2.【函數聲明】函數的字面量聲明有四個部分組成,但有些可以省略,列舉如下:
* function 關鍵字 * 可選名稱 ,可以匿名,但如果有必須為有效的JavaScript 標識符 * 括號內部一個以逗號分隔的參數列表 * 大括號括起來的函數體3.【name屬性】
有名稱的函數name屬性永遠為自己的名稱。關于匿名函數賦予變量后的name屬性是什么。。。我這里試出來ES5和ES6都是賦予后的變量名。
4.【函數調用與this的綁定】在這本《忍者秘籍》里給出了一個理解this綁定的方法:將this理解為運行上下文,this指的就是調用函數時的運行上下文。this的綁定實在運行時候確定的,而不是編譯時。(在《你不知道的JavaScript》中詳細講了四種this綁定,以后補~)
這里講了四種函數調用的方式:
作為一個函數進行調用
就是作為一般的函數,直接在全局上下文中調用。
作為一個方法進行調用
將函數作為一個對象中的方法進行調用,那么這時候this就會綁定在這個對象的上下文中。同時如果在全局中定義一個函數,賦值到對象的屬性中,其可以對不同對象進行操作,互不影響。
//全局的函數 function all () { console.log(this.a); } //第1個對象 let o1 = { a: "In1", func1: function () { return this.a }, func2: all } //第2個對象 let o2 = { a: "In2", func: all } let a = "Out" console.log(o1.func1()) //method in object In1 o1.func2() //this is o1 In1 o2.func() //this is o2 In2
作為構造器進行調用
也就是利用new運算符進行調用。構造器調用的時候會進行以下步驟:
1.創建一個新的對象 2.傳遞給構造器的參數的對象是this參數,從而成為構造器的函數上下文 3.如果沒有顯示的返回值,創建的對象則作為構造器的返回值返回 (即使有return值,作為3構造器調用的時候, 也返回新創建的對象,如果return的object,那就返回這個object) Ex:
function Ninja () { this.ninjaName = function () {} return 3 } let ninja1 = new Ninja() let ninja2 = new Ninja() //利用構造器得到兩個不同的對象 console.log(ninja1 === ninja2) //false //作為構造器調用時,返回的是對象而不是return值 console.log(ninja1) // Ninja { ninjaName: [Function] } //作為一般函數調用時,返回的是返回值 console.log(Ninja()) //3
通過apply()或call()方法進行調用
可以隨意改變函數的調用上下文,apply與call的區別是除第一個指定函數執行時
this綁定參數之外的參數。apply傳入參數數組,而call傳入全部參數。
函數實際傳入的參數和聲明時候的參數列表可以不同。
如果傳入的比聲明的少,那么沒有傳入數據的聲明將會是undefined,若多,則多出來的的傳入數據將無法通過變量名的方式訪問到。
參數可以通過函數的argument屬性訪問到,這是一個類數組,無法使用大多的數組的自帶方法。
對象內的匿名函數如果被賦值給了另一個對象,會產生引用丟失的問題。
7.【函數的屬性】函數作為對象,可以存儲一些參數值,以方便做一些特殊的處理。主要用途有函數存儲和自記憶函數。
函數存儲可以用來存儲要調用的函數,自記憶函數可以用來緩存函數已經運行過的結果,減少重復計算(自記憶函數可以通過閉包的方式進行再優化)
可以通過apply方法給出變長參數的數組。(ES6中可以使用...解包)
如:
let list = [7,9,1,2,0,10] Math.max(list[0],list[1]....) //此處省略,過于麻煩,要列出所有位置 Math.max.apply(Math, list) //利用apply可以直接傳入數組 Math.max(...list) //ES6中可以通過解包符直接傳入
通過閉包的特性,同樣可以實現通過參數個數不同的判斷,進行函數的重載。
閉包 1.【閉包的定義】閉包是一個函數在創建時允許自身訪問并操作該函數之外的變量時所創建的作用域。->聲明的函數什么時候都可以調用,即使是在作用域消失之后。
典型的閉包:
let outer = "ninja" let later function outerFunction () { let inner = "samurai" function innerFunction (paramValue) { console.log(inner) console.log(paramValue) console.log(toolate) } later = innerFunction } console.log(toolate) //undefined (用let會報錯) var toolate = "ronin" outerFunction() //創建閉包 later("wakizashi") //ninja wakizashi ronin 都輸出了
閉包創建了一個氣泡,保護了函數聲明那一時間點的作用域里的所有函數和變量,獲得了執行操作所需要的所有東西。
有三個有趣的結論:
內部函數的參數是包含在閉包中的
作用域之外的所有變量,即使是函數聲明之后,但是在函數被調用之前的那些聲明也都包含在閉包中
相同的作用域內,尚未聲明的變量不能使用(let聲明)、值為undefined(var聲明)
2.【閉包的用處】創建私有變量:利用function的特性,可以創建一個變量無法在外部直接訪問,需要用getter和setter,這兩個函數就是閉包的作用
回調和計時器:回調函數中可以通過閉包來訪問外部的變量
3.【綁定函數上下文(bind)】bind函數的用法與apply和call不同,簡化版的bind:
function bind (context, name) { return function () { return context[name].apply(context, arguments) } } //通過閉包的特性,來得到要綁定的函數 //系統bind的使用: functionName.bind(newThis)()4.【使用閉包實現的函數緩存記憶】
使用到的技巧:每個函數都有自己的上下文,所以函數從來都不是閉包的一部分。但是可以通過創建一個變量引用到這個上下文中(let fn = this,后面的function中可以利用fn調用到之前的this指向的上下文),從而將上下文也通過閉包保存起來。
具體的實現可以參見《忍者秘籍》書P103
5.【函數的即時調用】通常用于匿名函數,主要作用是可以保護作用域和變量名不污染全局。
并且可以解決迭代問題(同樣可以用來解決變量名稱過長的問題,將長名稱傳入即時調用的函數中,在函數中利用參數名進行操作):
for(let i = 0;i < div.length; i++) { (function(n) { div[n].addEventListener("click", function(){ alert("div #" + n + "was clicked") }, false) })(i) } //通過立即執行函數,直接把i和對應的div綁定,如果不這樣做,最后按所有的按鈕都會是最后一個i值(因為沒有實時綁定)
閉包肯定不止這么多內容,以后還會補充《你不知道的JavaScript》的內容
原型鏈JavaScript通過原型鏈實現繼承。
1.【prototype與new操作的共同使用】只有通過new操作產生的對象,可以使用構造器函數原型鏈上的內容,否則對象只能使用自己原型鏈上的內容。利用這個可以得出,利用構造器函數可以將JavaScript產生類似于類的概念。
2.【prototype的使用】prototype在對象創建之后如果有改動,所做的改動同樣會影響到已經創建的對象上去。
對于對象中的引用,先檢查是否在自己的聲明中存在,如果存在則用自己聲明的,如果不存在則循著原型鏈向上找,找到object根元素,如果仍然不存在,則返回undefined。
prototype其實是object隱藏屬性constructor的一個屬性,所以可以利用這個進行原型鏈,原型是實時附加在對象上的。
用一個對象的實例作為另一個對象的原型,調用方式如下
SubClass.prototype = new SuperClass()
這樣SubClass的實例不僅擁有原型,更有SuperClass中的所有屬性。并且instanceOf SuperClass也會判斷正確。
注意: 永遠不要使用SubClass.prototype = SuperClass.prototype,如果這樣做的話所有SubClass上的prototype修改都會影響到SuperClass上,會產生副作用。
4.【hasOwnProperty()方法】利用這個方法可以檢測屬性是否是原生就有的,而不是通過檢測原型鏈得到的。
5.【一些需要避免的場景】擴展Object的prototype,這樣會影響到所有的對象
擴展Number的prototype
產生原生對象的子類(盡量采用另外寫類,但同名方法通過prototype來調用原生對象方法的方法)
通過構造器創建對象不加new操作符(這樣做會有可能產生錯誤得不到對象并且污染全局變量)
這些就是讀完《忍者秘籍》的一部分感想和知識點,這本書最精華的部分應該就是這三塊的精煉描述了,這本書還講到了JavaScript的測試方法、正則表達式、定時器、運行時求值、事件與DOM操作以及一些跨瀏覽器的實踐方法。是一本好書(經常參加促銷的好書~),可以借來或者買來一讀~。
以上。
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/84809.html
摘要:前端日報精選免費的計算機編程類中文書籍英文技術文檔看不懂看印記中文就夠了的內部工作原理美團點評點餐前后端分離實踐讓你的動畫坐上時光機中文譯有多棒簡書譯別再使用圖片輪播了掘金譯如何在中使用掘金個讓增長成億美元公司的獨特方法眾成翻 2017-08-23 前端日報 精選 FPB 2.0:免費的計算機編程類中文書籍 2.0英文技術文檔看不懂?看印記中文就夠了!Virtual DOM 的內部工作...
摘要:設計模式是以面向對象編程為基礎的,的面向對象編程和傳統的的面向對象編程有些差別,這讓我一開始接觸的時候感到十分痛苦,但是這只能靠自己慢慢積累慢慢思考。想繼續了解設計模式必須要先搞懂面向對象編程,否則只會讓你自己更痛苦。 JavaScript 中的構造函數 學習總結。知識只有分享才有存在的意義。 是時候替換你的 for 循環大法了~ 《小分享》JavaScript中數組的那些迭代方法~ ...
閱讀 2612·2021-11-16 11:40
閱讀 3409·2021-11-08 13:26
閱讀 871·2021-10-28 09:32
閱讀 3530·2021-09-13 10:26
閱讀 803·2019-08-30 15:55
閱讀 777·2019-08-30 15:44
閱讀 1908·2019-08-30 15:44
閱讀 1756·2019-08-30 13:48