摘要:命令命令的作用,就是執行構造函數,返回一個實例對象。實例對象相當于創建一個空的構造函數,將其屬性指向參數對象,從而實現讓該實例繼承的屬性。
這是ES5的入門篇教程的筆記,網址:JavaScript教程,以下內容中黑體表示大標題,還有一些重點;斜體表示對于自身,還需要下功夫學習的內容。這里面有一些自己的見解,所以若是發現問題,歡迎指出~
實例對象與new命令
面向對象編程是目前主流的編程范式,它將真實世界各種復雜的關系,抽象為一個個對象,然后由對象之間的分工與合作,完成對真實世界的模擬。
對象是單個事物的抽象。
對象是一個容器,封裝了屬性(property)和方法(method)。
屬性是對象的狀態,方法是對象的行為。兩者最主要的區別在于屬性屬于對象靜態的一面,方法屬于對象動態的一面。
構造函數
面向對象編程的第一步,就是要生成對象,通常需要一個模板,表示某一類實物的共同特征,然后對象根據這個模板生成。
典型的面向對象編程語言(比如C++和JAVA),都有“類”(class)這個概念。所謂“類”就是對象的模板,對象就是“類”的實例。但是JavaScript語言的對象體系,不是基于“類”的,而是基于構造函數(constructor)和原型鏈(prototype)。
構造函數,為了與普通函數區別,構造函數名字的第一個字母通常大寫。它有兩個特點:1)函數體內部使用了this關鍵字,代表了所要生成的對象實例;2)生成對象的時候,必須使用new命令。
new命令
new命令的作用,就是執行構造函數,返回一個實例對象。
new命令本身就可以執行構造函數,所以后面的構造函數可以帶括號,也可以不帶括號。但是為了表示這里是函數調用,推薦使用括號。
// 推薦的寫法 let v = new Vehicle(); // 不推薦的寫法 let v = new Vehicle(); // 這兩種寫法都是等價的
new命令的原理
使用new命令時,它后面的函數依次執行下面的步驟:
1、創建一個空對象,作為將要返回的對象實例; 2、將這個空對象的原型,指向構造函數的prototype屬性; 3、將這個空對象賦值給函數內部的this關鍵字; 4、開始執行構造函數內部的代碼。
如果構造函數內部由return語句,而且return后面跟著一個對象,new命令會返回return語句指定的對象;否則,就會不管return語句,返回this對象。(也就是說,new后,只能返回對象,要么是自身,要么是一個新對象。)
構造函數與普通函數最主要的區別,是內部有沒有this關鍵字的函數。
對普通函數使用new命令,會返回一個空對象。
function getMessage() { let a = 1; return "this is a message"; } let msg = new getMessage(); // {} typeof msg // "object"
構造函數作為模板,可以生成實例對象,但是,有時拿不到構造函數,只能拿到一個現有的對象,通過Object.create()方法,可以將這個現有的對象作為模板,生成新的實例對象。
let person1 = { name: "張三", greeting: function() { console.log("Hi! I"m " + this.name + "."); } } let person2 = Obejct.create(person1); // person2繼承了person1的屬性和方法。 person2.name // 張三 person2.greeting() // Hi! I"m 張三.
this關鍵字
如果this所在的方法不在對象的第一層,這時this只是指向當前一層的對象,而不會繼承更上面一層。
由于this的指向是不確定的,所以切勿在函數中包含多層的this。
let a = { p: "Hello", b: { m: function() { console.log(this.p); } } }; a.b.m() // undefined a.b.m方法在a對象的第二層,該方法內部的this不是指向a,而是指向a.b。
let o = { f1: function () { console.log(this); let f2 = function () { console.log(this); }(); // 這里是執行函數了,就變成了值 } } o.f1() // Object 第一層指向對象o // Window 第二層指向全局對象 // 實際執行的如下 let temp = function () { console.log(this); }; let o = { f1: function() { console.log(this); let f2 = temp(); } }
數組中的map和foreach方法,允許提供一個函數作為參數,這個函數內部不應該使用this。因為兩者回調的this,是指向window對象的。(內層的this不指向外部,而是指向頂層對象)解決這種方法可以用中間變量,也可以將this當作foreach方法的第二個參數,固定運行環境。
JavaScript提供了call、apply、bind三個方法,來切換/固定this的指向。
函數實例的call方法,可以指定函數內部this的指向(即函數執行時所在的作用域),然后在所指定的作用域中,調用該函數。
call的第一個參數就是this所要指向的那個對象,后面餓參數則是函數調用時所需的函數。
apply方法的作用與call方法類似,也是改變this指向,然后再調用該函數。唯一的區別就是,它接收一個數組作為函數執行時的參數。
如果兩個方法沒有參數,或者參數為null或undefined,則等同于指向全局對象。
let obj = {}; let f = function () { return this; }; f() === window // true f.call(obj) === obj // true func.call(thisVlaue, arg1, arg2, ...) func.apply(thisValue, [arg1, arg2, ...]) function f(x, y) { console.log(x + y); } f.call(null, 1, 1) //2 f.apply(null, [1, 1]) //2 // JS不提供找出數組最大元素的函數,結合apply方法和Math.max方法,就可以返回數組的最大元素 let a = [10, 2, 4, 15, 9]; Math.max.apply(null, a) // 15
bind方法用于將函數體內的this綁定到某個對象,然后返回一個新函數。
let d = new Date(); d.getTime() // 1561974996108 let print = d.getTime; // 賦值后,內部的this已經不指向Date對象的實例了 print() // Uncaught TypeError: this is not a Date object. // 使用bind let print = d.getTime.bind(d); print() // 1561974996108
空元素(null)與undefined的差別在于,數組的forEach方法會跳過空元素,但是不會跳過undefined。
對象的繼承
大部分面向對象的編程語言,都是通過“類(class)”實現對象的繼承。傳統上,JavaSCript語言的繼承不通過class,而是通過“原型對象(prototype)”實現。
構造函數的缺點:同一個構造函數的多個實例之間,無法共享屬性,從而造成對系統資源的浪費。
function Cat(name, color) { this.name = name; this.color = color; this.meow = function () { console.log("喵喵"); }; } let cat1 = new Cat("大毛", "白色"); let cat2 = new Cat("二毛", "黑色"); cat1.meow === cat2.meow // false cat1和cat2是同一個構造函數的兩個實例,它們都具有meow方法。由于meow方法是生成在每個實例對象上面的,所以兩個實例就生成了兩次,沒有必要,也浪費了系統資源,需要共享,也就是JavaScript的原型對象(prototype)。
prototype屬性的作用
JavaScript繼承機制的涉及思想就是,原型對象的所有屬性和方法,都能被實例對象共享。也就是說,如果屬性和方法定義在原型上,那么所有實例對象就能共享,不僅節省了內存,還體現了實例對象之間的聯系。(感覺像是Java類中的公共屬性和公共方法一樣。。。)
JavaScript規定,每個函數都有一個prototype屬性,指向一個對象。對于普通函數來說,該屬性基本無用,但是,對于構造函數來說,生成實例的時候,該屬性會自動成為實例對象的原型。
function f() {} typeof f.prototype // "object" function Animal(name) { this.name = name; } Animal.prototype.color = "white"; // 原型對象上添加一個color屬性,下面的實例對象都共享了該屬性。 let cat1 = new Animal("大毛"); let cat2 = new Animal("二毛"); cat1.color // "white" cat2.color // "white" // 原型對象的屬性不是實例對象自身的屬性。只要修改原型對象,變動就立刻會體現在所有實例對象上 Animal.prototype.color = "yellow"; cat1.color // "yellow" cat2.color // "yellow" // 如果實例對象自身就有某個屬性或方法,它就不會再去原型對象尋找這個屬性或方法。 cat1.color = "black"; cat1.color // "black" cat2.color // "yellow"
綜上,原型對象的作用,就是定義所有實例對象共享的屬性和方法,而實例對象可以視作從原型對象衍生出來的子對象。
原型鏈
JavaScript規定,所有對象都有自己的原型對象(prototype)。一方面,任何我一個對象,都可以充當其他對象的原型;另一方面,由于原型對象也是對象,所以它也有自己的原型。因此,就會形成一個“原型鏈”(prototype chain):對象到原型,再到原型的原型……如果一層層地上溯,所有對象的原型最終都可以上溯到Object.prototype,即Object構造函數的prototype屬性。也就是說,所有對象都繼承了Object.prototype的屬性。這就是所有對象都有valueOf和toString方法的原因,因為這是從Object.prototype繼承的。
其實,Object.prototype對象也有他的原型,Object.prototype的原型是null。null沒有任何屬性和方法,也米有自己的原型。因此,原型鏈的盡頭就是null。
Object.getPrototypeOf(Object.prototype) // null
如果對象自身和它的原型,都定義了一個同名屬性,那么優先讀取對象自身的屬性,這叫做“覆蓋”(overriding)。舉例來說,如果讓構造函數的prototype屬性指向一個數組,就意味著實例對象可以調用數組方法。
constructor屬性
prototype對象有一個constructor屬性,默認指向prototype對象所在的構造函數。
constructor屬性的作用是,可以得知某個實例對象,到底是哪一個構造函數產生的。
function P() {} P.prototype.constructor === P // true constructor屬性定義在prototype對象上,意味著可以被所有實例對象繼承。 let p = new P(); p.constructor === P // true p是構造函數P的實例對象 p.constructor === P.prototype.constructor // true p.hasOwnPrototype("constructor") // false p自身沒有constructor屬性,該屬性是讀取原型鏈上面的P.prototype.constructor屬性
instanceof運算符
instanceof運算符返回一個布爾值,表示對象是否為某個構造函數的實例。(左邊是實例對象,右邊是構造函數。)
let v = new Vehicle(); v instanceof Vehicle // true 實際檢查右邊構建函數的原型對象(prototype),是否在左邊對象的原型鏈上。 // 等同于 Vehicle.prototype.isPrototypeOf(v) // instanceOf檢查的是整個原型鏈,因此同一個實例對象,可能會對多個構造函數都返回true let d = new Date(); d instanceof Date // true d instanceof Object // true d同時是Date和Object的實例 // 任意對象(除了null)都是Object的實例,所以instanceof運算符可以判斷一個值是否為非null的對象 typeof null // Object 是為了防止這種情況的發生 null instanceof Object // false // 但是需要注意的是,instanceof運算符只能用于對象,不適用原始類型的值 let s = "hello"; "s" instanceof String // false new String("s") instanceof String // true // 對于undefined和null,instanceof運算符總是返回false undefined instanceof Object // false null instanceof Object // false
emm,對象的繼承中的模塊內容還需要下狠功夫
Object對象的相關方法
Object.getPrototypeOf()方法返回參數對象的原型,這是獲取原型對象的標準方法。
let F = function () {}; let f = new F(); Object.getPrototypeOf(f) === F.prototype // true // 特殊對象的原型 // 空對象的原型是 Object.prototype Object.getPrototypeOf({}) === Object.prototype // true // Object.prototype 的原型是 null Object.getPrototypeOf(Objectprototype) === null // true // 函數的原型是 Function.prototype function f() {} Object.getPrototypeOf(f) === Function.prototype // true
Object.create()可以從一個實例對象,生成另一個實例對象。該方法接受一個對象作為參數,然后以它為原型,返回一個實例對象,該實例完全繼承原型對象的屬性。
// 原型對象 沒有this,所以不是構造函數!!!不能用new來創建,這是一個實例對象。 let A = { print: function () { console.log("hello"); } } // 實例對象 let B = Object.create(A); // 相當于創建一個空的構造函數,將其.prototype屬性指向參數對象A,從而實現讓該實例繼承A的屬性。 Object.getPrototypeOf(B) === A // true 以A對象為原型,生成了B對象,B繼承了A的所有屬性和方法 B.print() // hello B.print === A.print // true // 以下三種方式生成的新對象是等價的 let obj1 = Object.create({}); let obj2 = Object.create(Object.prototype); let obj3 = new Object(); //Object.create方法生成的新對象,動態繼承了原型。在原型上添加或修改任何方法,會立刻反映在新對象之上。 let obj1 = { p: 1 }; let obj2 = Object.create(obj1); obj1.p = 2; obj2.p // 2 修改對象原型obj1會影響到實例對象obj2 obj2.p = 3; obj1.p // 1 修實例對象obj2并不會影響到原型對象obj1 obj2.p // 3
length用來截斷長度,只對數組有效,對字符串無效。
emm 面向對象的編程也是云里霧里的,fighting!
異步操作概述
JavaScript只在一個線程上運行,也就是說,JavaScript同時只能執行一個任務,其他任務都必須在后面排隊等待。
程序里面所有的任務,可以分成兩類:同步任務(synchronous)和異步任務(asynchronous)。同步任務是那些沒有被引擎掛起、在主線程上排隊執行的任務,只有前一個任務執行完畢,才能執行后一個任務。異步任務是那些被引擎放在一邊,不進入主線程、而進入任務隊列的任務,只有引擎認為某個異步任務可以執行了(比如Ajax操作從服務器得到了結果),該任務(采用回調函數的形式)才會進入主線程執行。也就是說,異步任務不具有“堵塞”效應。
JavaScript運行時,除了一個正在運行的主線程,引擎還提供了一個任務隊列(task queue),里面是各種需要當前程序處理的異步任務。(實際上,根據異步任務的類型,存在多個任務隊列。為了方便理解,這里假設只存在一個隊列。)
引擎如何確定異步任務有沒有結果,能不能進入主線程呢?答案就是引擎在不停地檢查,一遍又一遍,只要同步任務執行完了,引擎就會去檢查那些掛起來的異步任務,是不是可以進入主線程了。這種循環檢查的機制,就叫做時間循環(Event Loop)。維基百科的定義是:“事件循環是一個程序結構,用于等待和發送消息和事件”(a programming construct that waits for and dispatches events or messages in a program)。
異步操作的模式:
1、回調函數 回調函數是異步操作最基本的方法。(包括Promise)
function f1() { // ... } function f2() { // ... } f1(); f2(); // 這樣編程的意圖是f2必須等到f1執行完成,才能執行。 // 但是如果f1是異步操作,f2會立即執行,不會等到f1結束再執行。這種情況下,可以考慮改寫f1,把f2寫成f1的回調函數。 function f1(callback) { // ... callback(); } function f2() { // ... } f1(f2); // 回調函數的優點是簡單、容易理解和實現,缺點是不利于代碼的閱讀和維護,各個部分之間高度耦合(coupling),使得程序結構混亂、流程難以追蹤(尤其是多個回調函數嵌套的情況),而且每個任務只能指定一個回調函數。
2、事件監聽
以前只記著是定時器,現在才知道,應該是事件監聽!!!還是需要多看!!!
另一種思路是采用事件驅動模式。異步任務的執行不取決于代碼的順序,而取決于某個事件是否發生。
f1.on("done", f2); // 當f1發生done事件,就執行f2。接著,對f1進行改寫 function f1() { setTimeout(function () { // ... f1.trigger("done"); // 表示執行完成后,立即觸發done事件,從而開始執行f2 }, 1000); }
這種方法比較容易理解,可以綁定多個事件,每個事件可以指定多個回調函數,并且可以“去耦合”(decoupling),有利于實現模塊化。缺點是整個程序都要編程事件驅動型,運行流程會變得很不清晰。閱讀代碼的時候,很難看出主流程。
3、發布/訂閱
事件完全可以理解成“信號”,如果存在一個“信號中心”,某個任務執行完成,就向信號中心“發布”(publish)一個信號,其他任務可以向信號中心“訂閱”(subscribe)整個信號,從而知道什么時候自己可以開始執行。這就叫做“發布/訂閱模式”(publish-subscribe parttern),又稱“觀察者模式”(observer pattern)。
觀察者模式還不是了解,還需要學習!!
定時器
JavaScript提供定時執行代碼的能力,叫做定時器(timer),主要由setTimeout()和setInterval()這兩個函數來完成。
1、setTimeout()
該函數用來指定某個函數或某段代碼,再多少毫秒之后執行。它返回一個整數,表示定時器的編號,以后可以用來取消這個定時器。
setTimeout函數接受兩個參數,第一個參數func|code是將要推遲執行的函數名或者一段代碼,第二個參數是推遲執行的毫秒數。
let timerId = setTimeout(func|code, delay); console.log(1); setTimeout("console.log(2)", 1000); // 需要注意的是,console.log(2)必須以字符串的形式,作為setTimeout的參數 console.log(3); // 1 // 3 // 2 function f() { console.log(2); } setTimeout(f, 1000); // 如果推遲執行的是函數,就直接將函數名,作為setTimeout的參數。
2、setInterval()
該函數的用法與setTimeout完全一樣,區別僅僅在于setInterval指定某個任務每隔一段時間就執行一次,也就是無限次的定時執行。
// 清除定時器 var id1 = setTimeout(f, 1000); var id2 = setInterval(f, 1000); clearTimeout(id1); clearInterval(id2);
3、實例:debounce函數
debounce防抖動,這是個好東西,以后要好好看看!!感覺可以用到小程序以及h5的輸入框輸入!!
4、setTimeout(f, 0)
setTimeout的作用是將代碼推遲到指定時間執行,如果指定時間為0,即setTimeout(f, 0),那么會立刻執行嗎?答案是不會,因為它必須要等到當前腳本的同步任務,全部處理完以后,才會執行setTimeout指定的回調函數f。也就是說,setTimeout(f, 0)會在下一輪事件循環一開始就執行。
setTimeout(function () { console.log(1); }, 0); console.log(2); // 2 // 1
Promise對象
Promise對象是JavaScript的異步操作解決方案,為異步操作提供統一接口。它起到代理作用(proxy),充當異步操作與回調函數之間的中介,使得異步操作具備同步操作的接口。
Promise對象的狀態:Promise對象通過自身的狀態,來控制異步操作。
Promise實例具有三種狀態:
1)異步操作未成功(pending)
2)異步操作成功(fulfilled)
3)異步操作失敗(rejected)
三種狀態里面,fulfilled和rejected合在一起稱為resolved(已定型)。
這三種狀態的變化途徑只有兩種:從“未完成”到“成功”;從“未完成”到“失敗”。
一旦狀態發生變化,就凝固了,不會再有新的狀態變化。這也是Promise這個名字的又來,它的英文意思是“承若”,一旦承諾成效,就不得再改變了。這也意味著,Promise實例的狀態變化只可能發生一次。因此,Promise的最終結果只有兩種:
1)異步操作成功,Promise實例傳回一個值(value),狀態變為fulfilled;
2)異步操作失敗,Promise實例拋出一個錯誤(error),狀態變為rejected。
JavaScript提供原生的Promise構造函數,用來生成Promise實例。
Promise構造函數接受一個函數作為參數,該函數的兩個參數分別是resolve和reject,它們是兩個函數,由JavaScript引擎提供,不用自己實現。
resolve函數的作用是,將Promise實例的狀態從“未完成”變為“成功”(即從pending變為fulfilled),在異步操作成功時調用,并將異步操作的結果,作為參數傳遞出去。
reject函數的作用是,將Promise實例的狀態從“未完成”變為“失敗”(即從pending變為rejected),在異步操作失敗時調用,并將異步操作報出的錯誤,作為參數傳遞出去。
DOM概述
DOM是JavaScript操作網頁的接口,全稱為“文檔對象模型”(Document Object Model)。它的作用是將網頁轉為一個JavaScript對象,從而可以用腳本進行各種操作(比如增刪內容)。
DOM的最小組成單位叫做節點(node)。文檔的屬性結構(DOM樹),就是由各種不同類型的節點組成,每個節點可以看作是文檔樹的一片葉子。
節點的類型有七種:
Document:整個文檔樹的頂層節點
DocumentType:doctype標簽(比如)
Element:網頁的各種HTML標簽(比如
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://m.specialneedsforspecialkids.com/yun/105283.html
摘要:今天閑來無事,看見幾行小字。又說所有對象,繼承終是。強行押韻一波這首詩的意思就是說的我今天沒有什么事情,然后無意中又在網上看到了任何對象都是從對象繼承而來的這句話。一時興起,便去驗證這句話。 今天閑來無事,看見幾行小字。又說所有對象,繼承終是Obj。—— 強行押韻一波 這首詩的意思就是說的我今天沒有什么事情,然后無意中又在網上看到了任何對象都是從Object對象繼承而來的這句話。一時興...
摘要:構造函數定義偵探類作為例子。里的既是類的定義,也是構造函數。在構造函數中定義的實例方法和屬性在每一個實例中都會保留一份,而在原型中定義的實例方法和屬性是全部實例只有一份。 無論React還是RN都已經邁入了ES6的時代,甚至憑借Babel的支持都進入了ES7。ES6內容很多,本文主要講解類相關的內容。 構造函數 定義偵探類作為例子。 ES5的類是如何定義的。 function ES5D...
摘要:函數默認值是一個很提高魯棒性的東西就是讓程序更健壯關于函數默認參數的描述函數默認參數允許在沒有值或被傳入時使用默認形參。也就實現了上邊三元運算符的功能。直接使用這種方式,省去了在函數內部進行默認值的檢查,能夠讓函數專注的做它應該做的事情。 函數默認值是一個很提高魯棒性的東西(就是讓程序更健壯)MDN關于函數默認參數的描述:函數默認參數允許在沒有值或undefined被傳入時使用默認形參...
摘要:但是在中,可以通過關鍵字來實現類的繼承的使用可以使得繼承意義更加明確并且值得一提的是,如果你使用來定義的組件,那么可以在類的構造器里面,用簡單的的聲明方式來替代方法。 原文:The 10 min ES6 course for the beginner React Developer譯者:Jim Xiao 著名的80/20定律可以用來解釋React和ES6的關系。因為ES6增加了超過75...
摘要:全局安裝本地安裝轉碼規則安裝完成后,我們可以看一下我們的文件,已經多了選項。直接在線編譯提供一個在線編譯器,可以在線將代碼轉為代碼。 古語有云:君子生非異也,善假于物;工欲善其事,必先利其器。 由于有些低版本的瀏覽器還是不支持ES6語法,學習ES6,首先要學會搭建一個基本的ES6開發環境,利用工具,把ES6的語法轉變成ES5的語法。 1、使用Babel把ES6編譯成ES5 1...
作者:陳大魚頭 github: KRISACHAN 前言 剛剛看了掘金上一篇文章《作為技術面試官,為什么把你pass了》,里面第一題就是用es5實現const,據作者反饋 這一題所有的面試者都沒有回答出來,感覺挺可惜的,其實這是一道比較簡單的題目,但是由于涉及到了一些Object對象屬性描述符的知識,這些描述符往往用到的場景不多,所以不容易記住。 屬性描述符: 對象里目前的屬性描述符有兩...
閱讀 1338·2021-11-25 09:43
閱讀 749·2021-11-18 10:02
閱讀 2874·2021-09-07 09:59
閱讀 2756·2021-08-30 09:44
閱讀 2926·2019-08-30 13:17
閱讀 2314·2019-08-29 12:17
閱讀 1680·2019-08-28 17:57
閱讀 1288·2019-08-26 14:04