寫在開頭: 准備自從更博以來每天更新一些新內容上去。就在前幾天連續3天每天一篇文章之后收到消息,大概意思是取消博文發布,請發布和程序員相關,原創的東西。看到這個消息有點滿臉悶逼啊,為啥子? 本來想:這是學習過程中的一些筆記,與大家共享,也提醒自己成長到了何種程度。但是乎,看到消息后,思前想后,想出了以下幾點原因:1.寫博客時未注明這些知識體系是自己學習筆記,在此,補上;第二:是我更新太快,被懷疑有水份。這也是停了兩天不更新的原因。開頭的最后,難關得過,繼續更新。
首先從js初級部分引入,包括語言構成,變量、數據類型、函數(簡單介紹)、面向對象(簡單介紹)
1. JS 基礎概要
1.1. 語言構成:
(1). 基礎語法(ECMAScript=ES 1,2,3,3.1,5(IE9+),6:ES2015)/
(2). BOM:Brower Object Model 瀏覽器對象模型
(3). DOM(W3C):Document Object Model 文檔對象模型
1.2. 變量
1.2.1. 變量聲明
變量的聲明:(var關鍵字) 會提升到當前作用域的頂部
1.2.2. 變量作用域
JS作用域:全局作用域、局部作用域(寫一個函數就產生了一個局部作用域)
1.3. 數據類型
1.3.1. 數據類型的分類
javaScritp的數據類型有:數值類型、字符串類型、布爾類型、null、undefined、對象(數組、正則表達式、日期、函數)
其中:
(1). 基本數據類型:數值、字符串、布爾、null、undefined (值類型)
(2). 復雜(復合)數據類型:對象 (引用類型)
typeof 可以檢測的數據類型有:
使用typeof可以檢測數值、字符串、布爾、undefined、函數
無法檢測函數以外的對象類型,以及null
1.3.2. 數據類型的轉換:
Number("12ab"); //NaN
parseInt("12.1ab"); //12
parseFloat("12.1ab"); // 12.1
1.4. 運算符
1.4.1. 算術運算符:
+、 -、 *、 /、 %(求余、取模)
1.4.2. 邏輯運算符:
&&、 ||、 !
注意:
&&:看運算符左邊的數是否為真值,如果為真值,返回右邊,如果為假值,返回左邊
||:看運算符左邊的數是否為真值,如果為真值,返回左邊,如果為假值,返回右邊
1.4.3. 真假值
假值:空字符串""/數字0/null/undefined/false/NaN
1.5. 語句
循環語句:for while do...while for...in
1.6. 函數
1.6.1. 函數聲明
(1). 聲明式函數——>函數聲明提前(會提升到當前作用域的頂部)
(2). 函數表達式(變量聲明提前)
注意:
如果同時存在變量聲明和函數聲明,那么聲明后的結果是一個函數
1.6.2. 函數參數和返回值
arguments獲取實參的相關信息
函數的返回值由return 語句決定
1.7. 異常
異常:JS代碼執行的時候出現的錯誤,出現錯誤之后,后面的代碼無法執行
異常捕獲:代碼出現了異常之后,進行手動的捕捉
異常捕獲的使用場景:一段代碼可能發生錯誤,這個錯誤現在沒時間解決,為了保證后面的代碼可以成功執行,就進行異常捕獲:try...catch
1.8. 面向對象初體驗
(1). JS是一門基於對象的多泛式語言
可以使用面向過程進行開發:
比如:獲取元素,綁定事件、設置樣式、完成動畫。。。。。。
(2). 可以使用面向對象的方式進行開發
面向(關注於)過程:基於函數,封裝函數
面向對象:關注點變成了對象
對象的概念:數據集,功能集
1.9. 面向對象一個例子
1 //CEO:安排一個任務給CTO(7天),CTO又把任務給PM(5天),PM又把任務給我了(3天),我去開發這個頁面 2 var ceo = { 3 assignTaskToCTO: function () { console.log("安排一個任務給CTO"); } 4 }; 5 var cto = { 6 assignTaskToPM: function () { console.log("安排一個任務給PM");} 7 }; 8 var pm = { 9 assignTaskToMe: function () { console.log("安排一個任務給我"); } 10 }; 11 var me = { 12 developWeb:function(){ console.log("我去開發這個頁面"); } 13 }; 14 //開發一個頁面 15 function deleveWeb(){ 16 ceo.assignTaskToCTO(); 17 cto.assignTaskToPM(); 18 pm.assignTaskToMe(); 19 me.developWeb(); 20 }
2. 構造函數(☆☆☆☆)
2.1. 為什么要有構造函數
一些對象具有相同的屬性和方法(特征和行為),將他們抽象出一個同一個類型,在JS中就需要通過一個構造函數來創建這些對象,在構造函數內部設置對象的屬性和方法
好處:一次封裝,多次調用,可省略一些代碼,也讓代碼更具有可讀性。
2.2. 獲取對象上的屬性和方法
(1). 屬性:
a. 實例.屬性名;
b. 實例["屬性名"];
(2).方法:
a. 實例.方法名;
b. 實例["方法名"];
2.3. 設置對象上的屬性和方法
(1). 屬性:
a. 實例.屬性名=新的值;
b. 實例["屬性名"]=新的值;
(2). 方法:
a. 實例.方法名=新的值;
b. 實例["方法名"]=新的值;
2.4. 刪除原型對象中的say方法
delete
2.5. 構造函數和普通函數
構造函數和普通函數只在調用方式不同
(1). 當成普通函數來調用
a. 函數內部的this指向調用的對象(如果沒有找到調用的對象,this指向window)
b. 函數的返回值由return語句決定,如果沒有說明函數沒有返回值(返回值是undefined)
(2). 當成了構造函數來調用會經歷以下過程
a. 創建一個該構造函數的實例
b. 將構造函數內部的this的值指向該實例
c. 執行函數體
d. 默認的返回值:該實例
(3). 函數調用的4種方式 function fn(){}
a. 普通調用:fn();
b. 當成構造函數調用:new fn();
c. 被對象調用:o.fn();
d. 上下文模式:call/apply
(4). 構造函數的返回值
a. 構造函數沒有手動添加返回值,返回構造函數的實例
b. 構造函數返回基本數據類型的值,返回的還是構造函數的實例
c. 構造函數返回對象類型的值,返回就是那個對象
3. 原型對象
原型對象:構造函數的prototype屬性:隨着實例化的次數增加,不同的對象他們擁有的say方法指向不同的內存,功能相同,造成了內存的浪費,為了解決內存,將這個方法放在某個對象(原型對象)中.
結論1:給構造函數的prototype屬性(對象)添加一個方法,這個方法就可以被構造函數的實例所共享
推論1:構造函數的prototype屬性(對象)上面的屬性、方法都可以被構造函數的實例所共享
推論2:Student.prototype.constructor===s1.constructor
結論2:構造函數的實例有一個__proto__指向的是構造函數的prototype屬性(原型對象) s1.__proto__===Student.prototype
總結:
a. 原型對象是構造函數的prototype屬性
b. 構造函數的實例的__proto__屬性指向原型對象
c. 原型對象有一個constructor屬性指向構造函數本身
4. 對象的屬性的讀取與設置
查找一個對象上的是否存在某個屬性的過程
a. 查找當前對象(s1)的內存中是否定義了該屬性,找到就停止查找
b. 去當前對象的__proto__屬性(原型對象)中去查找是否定義了該屬性,找到就停止查找
c. 如果2中沒找到,就去原型對象的原型對象中去查找是否定義了該屬性
s1.__proto__.__proto__
......
n. 找到某個對象(是沒有原型對象的:沒有__proto__屬性),如果這個對象中還沒有,確定了無法獲取該屬性
5. 繼承
5.0. 繼承的概念
a繼承自b——> a.__proto__===b
5.1. 擴展原型對象實現繼承
構造函數有一個prototype屬性(原型對象),通過給原型對象添加一個屬性、方法,從而可以讓構造函數的實例可以訪問到,這種繼承模型稱之為:擴展原型對象實現繼承。
1 function Person(){}; 2 Person.prototype.age = 20; 3 var p1 = new Person();
由於p1是Perosn的實例,所以p1是繼承自Person構造函數的原型對象。
原型圖如下:
5.2. 替換原型對象實現繼承
為什么要用替換原型對象實現繼承?
當我們需要給構造函數的原型對象中添加很多個屬性、方法的時候,如果一直使用擴展原型對象,多寫很多冗余(重復)的代碼
如何實現替換原型對象實現繼承?
重新給構造函數的prototype屬性賦值,值是一個全新的對象,在這個對象中添加屬性、方法;要注意一定要給這個對象添加constructor屬性,值指向構造函數本身
原型圖如下:
5.3. 混入繼承
混入繼承的使用場景:已知對象o,o2,需要將o中的功能(屬性、方法)拷貝到o2中 jQuery.extend() 方法;
實現方式:
1 for (var key in o) {//key是o的屬性名稱,key是字符串類型的值 2 //屬性的值: 3 var value=o[key]; 4 //設置o2中的同名屬性 5 o2[key]=value; 6 }
將混入繼承的模型封裝成函數
1 /** 2 * 混入繼承:將源對象中的屬性和方法拷貝到目標對象中 3 * @param target 目標對象:接收數據的對象 4 * @param source 源對象:數據從哪個對象來 5 */ 6 function mixin(target,source){ 7 for (var key in source) { 8 var value=source[key]; 9 target[key]=value; 10 //相當於: 11 //target[key]=source[key]; 12 } 13 return target; 14 }
jQuery中的$.extend()實現原理就是混入繼承
5.4. 原型+混入繼承
例子
1 function Cat(){} 2 Cat.prototype.extend=function(source){ 3 //調用實現混入繼承的函數往原型對象中添加屬性、方法 4 mixin(Cat.prototype,source); 5 };
原型+混入繼承在jQuery中也有很常見的應用:$.fn.extend()-->jQuery.prototype.extend()
5.5. 經典繼承
實現的功能:已知一個對象o,需要創建一個新的對象(o2),這個新的對象繼承自對象o
經典繼承的使用場景:要創建一個對象(不需要關心構造函數)新對象需要繼承自另一個指定的對象
1 //o2.__proto__===o; 將經典繼承封裝成一個函數: 2 function create(o){ 3 function F(){} 4 F.prototype=o; 5 return new F();//返回的就是F的實例 6 }
ES5(IE9以下版本不支持):Object.create()的實現原理就是源自於經典繼承
在舊的瀏覽器中應用經典繼承
1 //1、首先應該先檢測瀏覽器是否支持Object.create() 2 if(typeof Object.create !=="function"){ 3 Object.create=function(o){ 4 function F(){}//一個任意的構造函數 5 F.prototype=o;//設置構造函數的原型對象 6 return new F();//返回的就是F的實例 7 }; 8 }
6. 原型鏈
6.0. 基本概念
JS由對象組成,一個對象就有它的原型對象(__proto__),原型對象也有它的原型對象,一直到原型鏈的頂端,這樣構成了一個具有鏈條形狀的結構,稱之為原型鏈
__proto__該屬性可以被修改,但是無法被刪除
對象字面量的原型鏈圖
構造函數創建對象的原型鏈圖
數組對象的原型鏈圖
一般來說,無論是對象字面量,還是構造函數創建的對象、內置對象,基本包裝了類型的對象,2次原型查找(.__proto__)就可以找到
7. 一些屬性方法
7.1. Object.prototype.toString()
1 var obj={}; 2 console.log(obj.toString());//"[object Object]" 3 console.log(Object.prototype.toString.call([]));//"[object Array]" 4 console.log(Object.prototype.toString.call(/abc/));//"[object RegExp]" 5 console.log(Object.prototype.toString.call(new Date()));//"[object Date]" 6 console.log(Object.prototype.toString.call(1));//"[object Number]" 7 //1-->轉換為基本包裝類型的對象 8 console.log(Object.prototype.toString.call("abc"));//"[object String]" 9 console.log(Object.prototype.toString.call(true));//"[object Boolean]" 10 console.log(Object.prototype.toString.call(function(){}));//"[object Function]" 11 function Cup(){} 12 var cup=new Cup(); 13 console.log(Object.prototype.toString.call(cup));//"[object Object]"
使用Object.prototype.toString.call()只能檢測內置對象的類型——>$.type
7.2. Object.prototype.hasOwnProperty()
用來判斷方法的參數(字符串類型)是否是對象的自有屬性(屬性定義在對象自身的內存中——>通過原型鏈找到的屬性就不對了)
7.3. Object.prototype.isPrototypeOf()
表示當前對象是否是參數的原型對象(或在參數的原型鏈上)
7.4. Object.prototype.propertyIsEnumerable()
表示參數(字符串類型)是否是當前對象的可枚舉(for...in遍歷)屬性
7.5. Object.defineProperty(); //ES5誕生的
1 //參數1:需要定義屬性的對象 2 //參數2:屬性的名稱(字符串類型) 3 //參數3:屬性的描述符(對象) 4 value; 5 enumerable; //添加了一個不可枚舉屬性 6 writable; //添加一個不可使用賦值運算符的屬性 7 configurable; //值為true表示該屬性可以被刪除,值為false表示該屬性不可被刪除 8 get; //定義一個只讀(只能獲取值)屬性 9 set; // 定義一個只寫屬性,僅僅設置set
如:
1 Object.defineProperty(o1,"length",{ 2 enumerable:false, 3 value:"170" 4 });
7.6. a instanceof b
表示a是否為b(函數)的實例
8. 函數
8.1. 函數的創建
1 function f(){} //new Function()//任何函數都是通過Function創建出來的(任何函數都是Function的實例) 2 var f1=new Function("var a=1;var b=2;console.log(a+b);");//創建一個無參無返回值的函數 3 var f2=new Function("num","console.log(num);");f2(10);//創建一個有一個參數,無返回值的函數 4 var f3=new Function("a","b","c","return a+b+c; "); //創建一個具有3個參數,有返回值的函數 5 var f5=new Function("o","function F(){} " +"F.prototype=o;" +"return new F();");//創建一個經典繼承的函數
8.2. 函數的原型鏈
function f(){}
f();//f當成了普通函數來調用
new f();//f當成了構造函數來調用
一些結論:
a. 幾乎所有函數都有prototype屬性(Function.prototype這個函數上沒有)
b. 所有對象中都有__proto__屬性(Object.prototype該屬性的值null)
——> 幾乎所有函數都有prototype/__proto__屬性
c. 函數都是Function的實例(函數是通過Function創建出來的對象)
——> 自定義函數、Function、Array、RegExp、String、Boolean、Number、Object
d. 幾乎所有函數都是繼承自:Function.prototype(除了Function.prototype)
——> 函數.__proto__===Function.prototype
——> Object.__proto__===Function.prototype
——> Function.__proto__===Function.prototype
e. Function.prototype.__proto__===Object.prototype
String.prototype.__proto__===Object.prototype
Array.prototype.__proto__===Object.prototype
Boolean.prototype.__proto__===Object.prototype
Number.prototype.__proto__===Object.prototype
RegExp.prototype.__proto__===Object.prototype
下面的結果是:
function fn(){}
a、console.log(fn.constructor===Function);//true //查找fn的內存——>查找fn.__proto__(Function.prototype)——>Function
b、console.log(fn.__proto__===_______);//true //Function.prototype
c、console.log(Object.__proto__===______);//true //Function.prototype
d、console.log(Function.prototype===______);//true //Function.__proto__——>fn.__proto__——>Object.__proto__
e、console.log(Object.constructor); //Function
f、console.log(fn.prototype.constructor); //fn
g、console.log(Function.prototype.__proto__.constructor); //Object
原型鏈完整圖
9. 一個小栗子 ——
1 /*js*/ 2 /** 3 * Created by mhq on 2016/10/12. 4 */ 5 /*用來創建整個頁面的對象的構造函數,有一個參數*/ 6 function SongManager(parameter) { 7 /*頁面的初始化*/ 8 /*訪問原型對象中的方法,this指向構造函數的實例*/ 9 this.init(parameter); /*方法調用函數的方式調用的*/ 10 } 11 /*頁面的功能添加到對象的構造函數的原型對象中,以減少存儲空間,優化代碼*/ 12 /*替換原型對象的方法實現繼承:用一個新的對象替換原型對象,在這個新對象中添加功能*/ 13 SongManager.prototype = { 14 /** 15 * 頁面初始化 16 * @param parameter 用戶在頁面中傳過來的數據 17 */ 18 init: function (parameter) { // this指的是構造函數的實例 19 /*頁面渲染方法*/ 20 this.renderDom(parameter); 21 /*添加事件方法*/ 22 this.addEvents(); 23 }, 24 /** 25 * 渲染頁面 26 * @param parameter 27 */ 28 renderDom: function (parameter) { 29 /*首先放到一個容器里*/ 30 var html = "<div class='song_manager_container'>" + 31 "<h3>歌曲管理</h3>" + 32 "<div>" + 33 "<label>歌曲名:</label>" + 34 "<input type='text' id='songName' /> " + 35 "<label>歌手名:</label>" + 36 "<input type='text' id='singerName' /> " + 37 "<input type='button' value='添加' id='addSongBtn' /> " + 38 "<input type='button' value='刪除' id='deleteSongBtn'/>" + 39 "</div>" + 40 "<div class='song_manager_table'>" + 41 "<div class='first_column'>" + 42 "<span class='song_name'>歌曲名</span>" + 43 "<span class='song_name'>歌手名</span>" + 44 "<span class='song_name'>操作</span>" + 45 "</div>" + 46 "</div>" + 47 "</div>"; 48 /*將內容渲染到頁面中*/ 49 /*首先將整個容器添加到頁面對象的某個屬性,假設為songManagerContainer,方便綁定事件等*/ 50 this.songManagerContainer = $(html); 51 $(parameter.parent).append(this.songManagerContainer); 52 var _this = this; 53 /*渲染已有的歌曲到頁面中,添加歌曲*/ 54 $.each(parameter.data, function () { 55 /*這里的this指定是parameter.data中的對象*/ 56 _this.addSong(this); 57 }); 58 }, 59 /** 60 * 事件:點擊按鈕添加歌曲歌手;點擊按鈕刪除歌曲 61 */ 62 addEvents: function () { 63 /*這里需要保持this,即構造函數的實例*/ 64 var _this = this; 65 /*這里的數據是假設傳入的數據為對象*/ 66 this.songManagerContainer.on("click.addSongClick","#addSongBtn", function () { 67 var obj = {songName: _this.songManagerContainer.find("#songName").val(), singerName: _this.songManagerContainer.find("#singerName").val()}; 68 if (obj.songName && obj.singerName){ 69 /*調用添加歌曲方法,但是this此時指向了#addSongBtn這個dom元素*/ 70 _this.addSong(obj); 71 console.log(obj.songName); 72 _this.songManagerContainer.find("#songName").val(""); 73 _this.songManagerContainer.find("#singerName").val(""); 74 } 75 }).on("click.deleteSongClick",".deleteBtn", function (){ 76 console.log(this); 77 _this.deleteSong(this); 78 }); 79 }, 80 /** 81 * 添加歌曲、歌手功能 82 * @param obj 存儲需要添加的歌曲,歌手信息對象 83 */ 84 addSong: function (obj) { 85 var html = "<div class='song_list'>" + 86 "<span>"+obj.songName+"</span>" + 87 "<span>"+obj.singerName+"</span>" + 88 "<span>" + 89 "<input type='button' class='deleteBtn' value='刪除'/>" + 90 "</span>" + 91 "</div>"; 92 this.songManagerContainer.find(".song_manager_table").append(html); 93 }, 94 /** 95 * 刪除歌曲所在行功能 96 * @param deleteBtn 參數為刪除按鈕 97 */ 98 deleteSong:function(deleteBtn){ 99 $(deleteBtn).parent().parent().remove(); 100 } 101 }; 102 /*less*/ 103 104 @charset "UTF-8"; 105 .disInlineB{ 106 display: inline-block; 107 } 108 .m_0{ 109 margin: 0; 110 } 111 .lh-40{ 112 line-height: 40px; 113 } 114 .height-40{ 115 height: 40px; 116 } 117 .w_700{ 118 width: 700px; 119 } 120 .tac{ 121 text-align: center; 122 } 123 .song_manager_container { 124 margin: 40px auto; 125 .tac(); 126 .w_700(); 127 .song_manager_table{ 128 .w_700(); 129 .first_column{ 130 background: #0A7EC3; 131 margin: 10px 0; 132 .lh-40(); 133 .height-40(); 134 } 135 .song_list:nth-child(odd){ 136 background-color: #d0d0d0; 137 } 138 .song_list:nth-child(even){ 139 background-color: #0c6598; 140 } 141 span{ 142 width: 200px; 143 .disInlineB(); 144 } 145 } 146 .song_list span{ 147 .disInlineB(); 148 .height-40(); 149 .lh-40(); 150 } 151 } 152 /*index.html*/ 153 154 <!DOCTYPE html> 155 <html lang="en"> 156 <head> 157 <meta charset="UTF-8"> 158 <title>面向對象案例-表格渲染</title> 159 <link rel="stylesheet" href="css/index.css"> 160 </head> 161 <body> 162 <script src="js/lib/jquery-1.12.4.js"></script> 163 <script src="js/app/index.js"></script> 164 <script> 165 /*參數:要給哪個元素添加功能*/ 166 var data = [{songName: "斷點", singerName: "張敬軒"}, 167 {songName: "灰色空間", singerName: "羅志祥"}, 168 {songName: "我", singerName: "張國榮"}]; 169 var parameters = { 170 parent: document.body, 171 data: data 172 }; 173 /*將用戶傳入的多個參數放在一個對象中*/ 174 var songManger = new SongManager(parameters); 175 </script> 176 </body> 177 </html>
10. eval
函數是封裝了一段可以重復執行的代碼
eval方法的功能:執行一段JS代碼(封裝了代碼)
在eval方法中沒有作用域的概念(ES5嚴格模式有了獨立的作用域)——>聲明的變量都是全局變量,函數都是全局函數
eval和函數的比較
a. eval封裝了一段代碼(只能執行一次);函數封裝了一段代碼(可以重復執行)
b. eval中沒有獨立作用域的——>聲明的變量和函數都是全局的; 函數中是有獨立作用域 ——>函數內聲明的變量和函數只能在當前函數內部所訪問
使用場景:用來解析JSON數據(對於JSON的容錯率很高)
在解析對象的時候,要用到{},而在JS語言中,{}既可以表示對象,又可以表示代碼塊,在eval中遇到{}就會把它當成語句來處理,為了解決這個問題,在解析(單個對象)的時候,需要在解析的同時給字符串添加前后小括號,在解析數組的時候,就不用添加
11. 靜態屬性和實例屬性
(1). 給函數添加一個屬性(靜態屬性——>函數對象自身的屬性)
(2). 給某個構造函數的實例添加的屬性:實例屬性
12. 所有的函數對象都共有的一些靜態屬性
(1). name:獲取函數的名稱
(2). length:表示函數形參的個數
(3). caller:表示當前函數調用是在哪個函數內
13. arguments對象
arguments保存實參的相關信息
a. 獲取第一個實參:arguments[0]
b. 獲取實參的長度:arguments.length
arguments是一個偽數組(不是通過Array創建的),可以for循環像遍歷數組一樣來遍歷這個對象
arguments.callee是獲取當前函數本身,可以用來實現遞歸的功能;缺點:在ES5的嚴格模式中禁用
14. 嚴格模式
開啟嚴格模式:"use strict";
(1). 嚴格模式中禁止給一個未聲明的變量賦值:
(2). 嚴格模式中eval具有了獨立作用域——>在eval中聲明的變量和函數都是局部變量
(3). 嚴格模式中禁止使用arguments.callee進行遞歸調用
15. 遞歸:函數自己調用自己
15.1. 計算斐波那契數列第n項的值:1,1,2,3,5,8,13...
1 function fibonacci(n){ 2 if(n==1 || n==2) return 1; 3 return fibonacci(n-1)+fibonacci(n-2); 4 } 5 for (var i = 0; i < 10; i++) { console.log(fibonacci(i+1)); }
15.2. 遞歸計算階乘
1 function factorial(n){ 2 if(n<0) return 0; //為了防止報錯 3 if(n==0) return 1; //遞歸的結束條件:0的階乘為1 4 return factorial(n-1)*n; 5 } 6 for (var i = 0; i < 10; i++) { console.log("數字:"+i); console.log(factorial(i));}
15.3. m的n次方
1 function pow(n, m) { 2 if (m === 0) return 1; 3 if (m < 0) return 1 / (pow(n, -(m + 1)) * n); 4 else if (m > 0) return pow(n, m - 1) * n; 5 } 6 for (var i = -2; i <= 0; i++) { 7 console.log(pow(2, i)); 8 }
15.4. 遞歸查找父元素
需求:要判斷一個div是否在另一個div的下面
1 function find(child,parent){ 2 //實現思路:由子元素一級一級的查找父元素 3 //遞歸的結束條件:查到了文檔的根節點、找到了父元素 4 if(child.parentNode===parent) return true; //說明已經找到了符合條件的父元素 5 if(child.parentNode===null) return false; //說明已經查找到了文檔的根節點 6 return find(child.parentNode,parent); 7 //第1次執行find——>child.parentNode===parent 8 //第2次執行find——>child.parentNode.parentNode===parent 9 //第3次執行find——>child.parentNode.parentNode.parentNode===parent 10 } 11 console.log(find(d3,d10));//false 12 console.log(find(d3,d1));//true
在chrome瀏覽器中,如果是為了調試的方便,對於頁面中的id元素,可以不獲取,直接編寫代碼
16. 詞法分析
16.1. JS程序執行過程
(1). 讀取代碼,主關注聲明的部分:var
(2). 判斷var后面的名字是否已經被標記,如果沒有被標記過,就標記
(3). 讀取完畢后,代碼從上往下,從左往右依次執行
16.2. 詞法作用域(作用域:變量可以使用到不能使用的范圍)
詞法作用域就是描述變量的訪問范圍:
(1). 在代碼中只有函數可以限定作用范圍,允許函數訪問外部的變量
(2). 在函數內優先訪問內部聲明的變量,如果沒有才會訪問外部的
(3=. 所有變量的訪問規則,按照預解析規則來訪問
作用域鏈:每一個函數具有獨立作用域,由於函數內可以套函數,所以在函數內部訪問變量的時候,需要一級一級的往上查找該變量,這樣就好像構成了一個鏈式結構,把它稱之為作用域鏈
16.3. 閉包(閉包解決一些實際問題:利用函數可以跨作用域訪問變量的特性)
16.4 閉包實現思路:外層函數,內層函數
通常設置外層函數的返回值就是內層函數
也可以讓外層函數的返回值是一個對象(方法)
如果需要保存一個數據(外層函數的同一個變量),讓內層函數調用多次,該變量的值都是共享的
如果需要保存多個數據(外層函數的同一個變量),讓外層函數調用多次
17. 函數調用、this指向、返回值
一個函數最終產生什么樣的結構,跟如何調用這個函數息息相關:函數的四種調用模式
17.1. 4種調用模式
(1). 第一種模式:函數調用模式,也就是寫一個函數,然后調用一下
(2). 第二種模式:方法調用模式,也就是將函數成為對象的一個方法,然后通過對象來調用
(3). 第三種模式:構造函數調用模式,也就是將函數當成構造函數來調用
(4). 第四種調用模式:上下文調用模式:根據調用方式的不同可以產生不同的結果
實現方式:call/apply (apply和call的唯一區別是第二個參數是數組,將實參值一一傳到數組中。fn.call(函數內部的this的值,實參1,實參2...))
17.2 不同調用模式中的this的值
(1). 函數調用模式中this指向:window
(2). 方法調用模式中this指向:調用的對象
(3). 構造函數調用模式中this指向:構造函數的實例
(4). 上下文調用模式中this指向:
a. 如果call方法的第一個參數是一個對象,則fn函數內部的this的值指向該對象
b. 如果call方法的第一個參數是一個字符串、數字、布爾值,則fn函數內部的this的值會轉換為該類型所對應的基本包裝類型的對象
c. 如果call方法的第一個參數是null,則fn函數內部的this的值是window——>就相當於是一次函數調用模式
17.3 調用模式中的返回值
(1). 函數調用模式中返回值:由return語句決定
(2). 方法調用模式中返回值:由return語句決定
(3). 構造函數調用模式中的返回值:
a. 如果構造函數沒有手動設置返回值,那么會返回構造函數的實例
b. 如果手動給構造函數添加了返回值,有以下2種情況:
(a). 返回值是值類型:最終的返回值還是構造函數的實例
(b). 返回值是引用類型(對象):最終的返回值就是該對象
(4). 上下文調用模式中的返回值:由return語句決定
17.4 call/apply區別
1.相同點:
(1) 都是Function.prototype對象中定義的方法
(2) 第一個參數都是表示函數內部的this的值
2. 不同點:
如果需要給函數傳遞參數的時候:
利用call方法,將函數的參數從第二個參數開始依次排開
apply方法的第二個參數是一個數組對象,數組的第一個參數表示函數的第一個實參,依次以此類推
17.5 apply的一個漂亮的應用
1 var points = [ 2 { x: 110, y: 50}, { x: 130, y: 60 }, { x: 20, y: 70 }, { x: 60, y: 50 } 3 ]; 4 var maxX = Math.max.apply( null, points.map(function (v) { return v.x; }));
以上代碼中借用Math對的max方法,利用arr.map()方法中返回的是數組這一特性得到了數組中對象的某個屬性的最大值;
18. 借用構造函數實現繼承
定義:子類構造函數(Student)借用父類構造函數(Person)來完成:給子類的實例添加屬性
注意條件:由於要借用父類構造函數,所以父類構造函數的功能要對子類對象通用