深入理解javascript原型和閉包

原文鏈接http://www.cnblogs.com/wangfupeng1988/p/3977924.html

對象是屬性的集合。

function show(x) {console.log(typeof(x));    // undefinedconsole.log(typeof(10));   // numberconsole.log(typeof('abc')); // stringconsole.log(typeof(true));  // booleanconsole.log(typeof(function () { }));  //functionconsole.log(typeof([1, 'a', true]));  //objectconsole.log(typeof ({ a: 10, b: 20 }));  //objectconsole.log(typeof (null));  //objectconsole.log(typeof (new Number(10)));  //object}
show();

以上代碼列出了typeof輸出的集中類型標識,其中上面的四種(undefined, number, string, boolean)屬于簡單的值類型,不是對象。剩下的幾種情況——函數、數組、對象、null、new Number(10)都是對象。他們都是引用類型。
判斷一個變量是不是對象非常簡單。值類型的類型判斷用typeof,引用類型的類型判斷用instanceof。

對象都是通過函數來創建的

函數就是對象的一種,因為通過instanceof函數可以判斷。

var fn = function () { };
console.log(fn instanceof Object);  // true
var obj = { a: 10, b: 20 };
var arr = [5, 'x', true];

這是種語法糖,是一種快捷方式。真實代碼是:

//var obj = { a: 10, b: 20 };//var arr = [5, 'x', true];var obj = new Object();obj.a = 10;obj.b = 20;var arr = new Array();arr[0] = 5;arr[1] = 'x';arr[2] = true;

而其中的 Object 和 Array 都是函數。說明一切對象都是由函數創建的。
對象是函數創建的,而函數卻又是一種對象

typeof(Array)//"function"
typeof([1,2])//"object",[1,2]其實是new Array();
typeof(function() {})//"function"

prototype原型

函數有prototype屬性,對象有_proto_屬性。
每個函數都有一個屬性叫做prototype。這個prototype的屬性值是一個對象(屬性的集合,再次強調!),默認的只有一個叫做constructor的屬性,指向這個函數本身。
如上圖,SuperType是是一個函數,右側的方框就是它的原型。
clipboard.png
接著往下說,你也可以在自己自定義的方法的prototype中新增自己的屬性

function Fn() { }
Fn.prototype.name = '王福朋';
Fn.prototype.getYear = function () {return 1988;
};

還有一種很容易被混淆的定義函數方法:

var func = function() {}
func.prototype.name = "prototype test";
func.prototype.protoFunc = function() {console.log("protoFunc")};
func.a = function() {console.log("定義對象的屬性")}
func.a();//func是個對象,func是個引用。
var funcObject = new func();
console.log(new func(), "new func()");//function() {}
console.log(funcObject, "funcObject");//function() {}
funcObject.name;
funcObject.protoFunc();
funcObject.a();//funcObject.a is not a function(…),//func是個對象,有a這個屬性,
//funcObject是個對象,但是a不是funcObject的屬性。func的所有prototype的屬性就是funcObject的屬性。
function Fn() {};
Fn.prototype.protoFunc = function() {return "aa";console.log("protoFunc")};
Fn.a = function() {}
var fn= new Fn();
fn.protoFunc() //"aa"
fn.a();//FnObject 不是Fn對象,所以a()方法沒定義。

即,Fn是一個函數,fn對象是從Fn函數new出來的,這樣fn對象就可以調用Fn.prototype中的屬性。
因為每個對象都有一個隱藏的屬性——“__proto__”,這個屬性引用了創建這個對象的函數的prototype。即:fn.__proto__ === Fn.prototype.這里的"__proto__"成為“隱式原型”

繼承(原型鏈)

function Foo() {}
var f1= new Foo();
f1.a = 10
Foo.prototype.a = 100;
Foo.prototype.b = 200;
console.log(f1.a)//10
console.log(f1.b)//200

f1是Foo函數new出來的對象,f1.a是f1對象的基本屬性,f1.b是怎么來的呢?——從Foo.prototype得來,因為f1.__proto__指向的是Foo.prototype
訪問一個對象的屬性時,先在基本屬性中查找,如果沒有,再沿著__proto__這條鏈向上找,這就是原型鏈。

靈活的原型鏈

如果你要添加內置方法的原型屬性,最好做一步判斷,如果該屬性不存在,則添加。如果本來就存在,就沒必要再添加了。

繼承--原型鏈

clipboard.png

理解這張圖有以下幾點:
1.一切皆對象,所以每個對象都有_proto_屬性,_proto_屬性指向創建該對象的函數的prototype。
2.prototype的屬性值是一個對象(默認的只有一個叫做constructor的屬性,指向這個函數本身)。所以各函數的prototype也有_proto_屬性。
3.Object函數的_proto_只想null。
4.var Foo = new Function();則Foo._proto_指向Function.prototype.
5.function Function(){}是有它自己創造的,所有Function._proto_指向Function.prototype.

執行上下文

在“準備工作”中完成了哪些工作:

console.log(a)//a is not definedconsole.log(a)//undefined
var a;console.log(a)//undefined
var a = 10;console.log(a)//function a() {}函數聲明
function a() {}console.log(a)//undefined 函數表達式
var a = function(){}console.log(this)//Window {external: Object, chrome: Object, document: document, lock: Object, SF: Object…} 
//this在任意環境下都有值
  1. 變量、函數表達式--變量聲明,默認賦值為undefined;

  2. this——賦值

  3. 函數聲明——賦值;
    這三種數據的準備情況我們稱之為“執行上下文”或者“執行上下文環境”。

clipboard.png

給執行上下文環境下一個通俗的定義——在執行代碼之前,把將要用到的所有的變量都事先拿出來,有的直接賦值了,有的先用undefined占個空。

this

在函數中this到底取何值,是在函數真正被調用執行的時候確定的,函數定義的時候確定不了。因為this的取值是執行上下文環境的一部分,每次調用函數,都會產生一個新的執行上下文環境。
情況1:構造函數
所謂構造函數就是用來new對象的函數。其實嚴格來說,所有的函數都可以new一個對象,但是有些函數的定義是為了new一個對象,而有些函數則不是。另外注意,構造函數的函數名第一個字母大寫(規則約定)。例如:Object、Array、Function等。

function Foo() {this.name = "name";this.year = "year";console.log(this);
}var f1 = new Foo();//Foo {name: "name", year: "year"}
Foo();//Window {external: Object, chrome: Object, document: document, lock: Object, SF: Object…}

如果函數作為構造函數用,那么其中的this就代表它即將new出來的對象。
如果直接調用Foo函數,而不是new Foo(),這種情況下this是window。

情況2:函數作為對象的一個屬性

var obj = {x:10,fn:function(){console.log(this);    console.log(this.x);}
}var fn1 = obj.fn;
fn1();//Window {external: Object, chrome: Object, document: document, lock: Object, SF: Object…} undefined
obj.fn();//Object {x: 10}  10

fn不僅作為一個對象的一個屬性,而且的確是作為對象的一個屬性被調用。結果this就是obj對象。
注意,如果fn函數不作為obj的一個屬性被調用。如上代碼,如果fn函數被賦值到了另一個變量中,并沒有作為obj的一個屬性被調用,那么this的值就是window,this.x為undefined。

情況3:函數用call或者apply調用
當一個函數被call和apply調用時,this的值就取傳入的對象的值。

var obj = {x : 1
}var fn = function() {console.log(this);console.log(this.x);
}fn.call(obj);//Object {x: 1} 1

情況4:調用普通函數

var obj = {x:10,fn:function(){console.log(this);    //Object {x: 10}console.log(this.x);    //10function f() {console.log(this);    // Window {external: Object, chrome: Object, document: document, lock: Object, SF: Object…}console.log(this.x);//undefined}f();}
}obj.fn();

函數f雖然是在obj.fn內部定義的,但是它仍然是一個普通的函數,this仍然指向window。

簡介【作用域】

你光知道“javascript沒有塊級作用域”是完全不夠的,你需要知道的是——javascript除了全局作用域之外,只有函數可以創建的作用域。
所以,我們在聲明變量時,全局代碼要在代碼前端聲明,函數中要在函數體一開始就聲明好。除了這兩個地方,其他地方都不要出現變量聲明。而且建議用“單var”形式。
jQuery源碼的最外層是一個自動執行的匿名函數:

clipboard.png
為什么要這樣做呢?

原因就是在jQuery源碼中,聲明了大量的變量,這些變量將通過一個函數被限制在一個獨立的作用域中,而不會與全局作用域或者其他函數作用域的同名變量產生沖突。
全世界的開發者都在用jQuery,如果不這樣做,很可能導致jQuery源碼中的變量與外部javascript代碼中的變量重名,從而產生沖突。

閉包

但是你只需要知道應用的兩種情況即可——函數作為返回值,函數作為參數傳遞。

function fn(){var max = 10;return function bar(x){if(x > max) {console.log(x);}};
}var f1 = fn();
f1(15);//15

如上代碼,bar函數作為返回值,賦值給f1變量。執行f1(15)時,用到了fn作用域下的max變量的值。

第二,函數作為參數被傳遞

var max = 10;
fn = function(x){if(x > max) {console.log(x);}
};(function(f) {var max = 100;f(15);//15
})(fn);

如上代碼中,fn函數作為一個參數被傳遞進入另一個函數,賦值給f參數。執行f(15)時,max變量的取值是10,而不是100。
自由變量跨作用域取值時,要去創建這個函數的作用域取值,而不是“父作用域”。
當一個函數被調用完成之后,其執行上下文環境將被銷毀,其中的變量也會被同時銷毀。但是有些情況下,函數調用完成之后,其執行上下文環境不會接著被銷毀。這就是需要理解閉包的核心內容。
對于這個例子

function fn(){var max = 10;return function bar(x){if(x > max) {console.log(x);}};
}var f1 = fn();
max = 100;
f1(15);//15

fn()調用完成。按理說應該銷毀掉fn()的執行上下文環境,但是這里不能這么做。注意,重點來了:因為執行fn()時,返回的是一個函數。函數的特別之處在于可以創建一個獨立的作用域。而正巧合的是,返回的這個函數體中,還有一個自由變量max要引用fn作用域下的fn()上下文環境中的max。因此,這個max不能被銷毀,銷毀了之后bar函數中的max就找不到值了。

總結:跟著大牛的文章,跟著理解,跟著寫代碼,終于把閉包理解了。但是,理論是學到了,真正用的時候還得要多思考。這種邏輯的碰撞就是程序員向前最大的鼓勵。謝謝大牛。
寫給自己的話:過早地開始關注細節,你很可能錯失上下文或整體信息。當然,錯失了細節,也會讓你的理解僅僅停留在一些事物的表面。

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/284528.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/284528.shtml
英文地址,請注明出處:http://en.pswp.cn/news/284528.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

薪資高壓線

閱讀本文大概需要5分鐘。最近一名讀者咨詢一個問題:洋哥,最近公司有一名同事因為打探其他人薪資被開除了,為啥我們公司要把薪資設置為高壓線。這是個好問題,解答完他的疑惑后想起了一年多前寫過一篇,彼時讀者還比較少&…

達摩院年終預測出爐:2022 十大科技趨勢,AI for Science 高居榜首

作為“一所探索科技未知的研究院”,阿里巴巴達摩院成立至今已經四年了。 這四年來,達摩院秉持著“探索科技位置,以人類愿景為驅動力,開展基礎科學和顛覆式技術創新研究”的原則與使命,在基礎科研和硬科技發展上“遍地生…

chrome調試工具高級不完整使用指南(基礎篇)

一、前言 本文記錄的是作者在工作上面對chrome的一些使用和情況的分析分享,內容僅代表個人的觀點。轉發請注明出處(http://www.cnblogs.com/st-leslie/),謝謝合作 二、瀏覽器模塊介紹 由于chrome瀏覽器一直在不斷的進行更新迭代,會不斷的新增功能&#x…

新型基礎測繪與實景三維中國建設技術文件【2】基礎地理實體分類、粒度及精度基本要求

《新型基礎測繪體系建設試點技術大綱》指出,新型基礎測繪將以“基礎地理實體”為核心的成果模式創新為切入點,帶動技術體系、生產組織體系和政策標準體系的全面創新,從而實現基礎測繪高質量發展。 基礎地理實體作為新型基礎測繪產品體系的核心…

構建和實現單點登錄解決方案(轉載于IBMdeveloperWorks)

將一個開放源碼的基于 Java 的身份驗證組件集成進 Web 門戶中 在現有的應用程序中實現單點登錄解決方案(single sign-on,SSO,即登錄一次,就可以向所有網絡資源驗證用戶的身份)是非常困難的,但是在構建復雜的…

分享一個基于Abp 和Yarp 開發的API網關項目

這個項目起源于去年公司相要嘗試用微服務構建項目,在網關的技術選型中,我們原本確認了ApiSix 網關,如果需要寫網關插件需要基于Lua腳本去寫,我和另外一個同事當時基于這個寫了一個簡單的插件,但是開發測試以及發布都很麻煩,而且使用Lua腳本作為插件的開發語言本身也不是我們強項…

羅振宇2022“時間的朋友”跨年演講全文稿(pdf)

2021年12月31日20:30,五糧液成都金融城演藝中心,羅振宇“時間的朋友”跨年演講如約而至。 羅胖曾發下大愿望:跨年演講要連辦二十年。今年是第七場,也是最特殊的一場,羅胖面對12000個空座位,用53個好故事&am…

08.LoT.UI 前后臺通用框架分解系列之——多樣的Tag選擇器

LOT.UI分解系列匯總:http://www.cnblogs.com/dunitian/p/4822808.html#lotui LoT.UI開源地址如下:https://github.com/dunitian/LoTCodeBase/tree/master/LoTUI 這個必須說下,本來是用Bootstrap-Select做的,很漂亮,正好…

jquery文檔加載完畢后執行的幾種寫法

2019獨角獸企業重金招聘Python工程師標準>>> 1.js文檔加載完畢 標簽內 οnlοad"test()"window.οnlοadfunction(){}2.jquery文檔加載完畢 //方式1 $(document).ready(function(){//TODO }); //方式2 $(function(){//TODO }) //方式3 $(function($){//TO…

新型基礎測繪與實景三維中國建設技術文件【3】基礎地理實體空間身份編碼規則

基礎地理實體是新型基礎測繪產品體系中的核心成果,是推動基礎測繪工作轉型升級的關鍵。與現有的測繪地理信息數據不同,基礎地理實體具有多粒度、多模態、多層次,以及搭載結構化、半結構化和非結構化多樣化信息的鮮明特點。 基礎地理實體空間…

oracle 表 視圖 存儲過程 序列 job

table 表--delete tabledrop table Test1;-- Create tablecreate table TEST1(ID NUMBER,T_NAME VARCHAR2(100),DT DATE);-- 添加注釋comment on column TEST1.T_NAME is 名稱;--添加age字段alter table Test1 add (age NUMBER(8));--刪除字段alter table TABLE_NAME …

[轉]Docker 大勢已去,Podman 即將崛起

Podman Podman 什么是Podman?Podman和Docker的主要區別是什么?Podman的使用與docker有什么區別?Podman 常用命令 容器鏡像部署 PodmanPodman 加速器使用 Podman 運行一個容器列出運行的容器檢查正在運行的容器查看一個運行中容器的日志查看一…

基于Kubernetes v1.24.0的集群搭建(一)

一、寫在前面 K8S 1.24作為一個很重要的版本更新,它為我們提供了很多重要功能。該版本涉及46項增強功能:其中14項已升級為穩定版,15項進入beta階段,13項則剛剛進入alpha階段。此外,另有2項功能被棄用、2項功能被刪除。…

mvc設計模式和mvc框架的區別

一組概念需要先理解,因為后面需要用: 架構:簡單的說架構就是一個藍圖,是一種設計方案,將客戶的不同需求抽象成為抽象組件,并且能夠描述這些抽象組件之間的通信和調用。 框架:軟件框架是項目軟件…

新型基礎測繪與實景三維中國建設技術文件【4】基礎地理實體數據元數據

基礎地理實體數據是新型基礎測繪產品體系中的核心成果,為實現該數據的規范化管理和使用,編制基礎地理實體數據元數據技術文件。本文件規定了基礎地理實體數據元數據的基本要求和元數據內容,適用于二維表達形式、三維表達形式基礎地理實體數據…

思科三層交換機充當路由器實現全網互通

轉載于:https://blog.51cto.com/13568840/2059797

劍指offer編程題Java實現——面試題3二維數組中的查找

題目描述 在一個二維數組中,每一行都按照從左到右遞增的順序排序,每一列都按照從上到下遞增的順序排序。請完成一個函數,輸入這樣的一個二維數組和一個整數,判斷數組中是否含有該整數。下面是我實現的代碼,修改下類名(…

mpvue開發小程序分享朋友圈無法自定義標題解決方法

在node_modules里面找到mpvue,手動修改一下mpvue這個包下的index.js文件 // 用戶點擊右上角分享 到朋友圈 onShareTimeline: rootVueVM.$options.onShareTimeline? function (options) { return callHook$1(rootVueVM, onShareTimeline, options); } : null,找到 L…

【ArcGIS Pro微課1000例】0020:ArcGIS Pro中河流(曲線)、湖泊(水體色)圖例制作案例教程

相關閱讀:【ArcGIS微課1000例】0032:ArcGIS中河流(曲線)、湖泊(水體色)圖例制作案例教程 河流、湖泊的樣式設置功能在ArcGIS Pro得到了延續,本文講解ArcGIS Pro中河流湖泊圖例的設置方法。 《ArcGIS Pro從入門到精通系列精品教程(微課版)》專欄包括完整的實驗數據包,…

swift學習選pizza項目

2019獨角獸企業重金招聘Python工程師標準>>> 原文: https://makeapppie.com/2014/09/18/swift-swift-implementing-picker-views/ 效果: 步驟: 新建iOS single view application 名字為SwiftPickerViewPizzaDemo, 打開main storyboard選中view controoler, 右上角, …