文章目錄
- 摘要
- 1 引言
- 2 基礎
- 2.1 嵌入網頁
- 2.2 基本語法
- 2.3 資料型別
- 2.3.1 基本資料型別
- 2.3.2 高級資料型別
- 2.4 程式結構
- 2.4.1 順序結構
- 2.4.2 選擇結構
- 2.4.3 回圈結構
- 3 函式
- 3.1 函式定義
- 3.1.1 定義函式的兩種方式
- 3.1.2 函式引數
- 3.2 變數作用域
- 3.3 變數提升
- 3.4 全域作用域
- 3.5 命名空間
- 3.6 解構賦值
- 3.7 方法
- 3.8 高階函式
- 3.8.1 map/reduce
- 3.8.2 filter
- 3.8.3 sort
- 4 面向物件
- 4.1 JS創建物件的方式
- 4.2 原型物件(prototype)和物件原型(_ _ proto _ _)
- 4.3 原型物件(prototype)中的constructor屬性
- 4.4 原型鏈實作繼承
- 4.4.1 call方法
- 4.4.2 使用call繼承
- 4.4.3 原型鏈詳解
- 4.4.4 通過“中間人”鏈接原型鏈
- 4.5 class語法糖
- 4.5.1 class實作繼承
摘要
下面文章中使用JS代替JavaScript
1 引言
JavaScript(以下統一簡稱JS)是目前最流行的腳本語言,沒有之一,在你的手機,pad,電腦等互動邏輯都是通過JS實作的,
JS是一種運行在瀏覽器中的解釋型的編程語言,nodejs將JS移植到了服務端,這讓JS成為了全能戰士
只要你想接觸前端,JS是你繞不開的話題,在web前端領域,JS是絕對的頂流
JS 歷史
1995年,還是一個靜態網頁時代,當時的網景公司憑借Navigator瀏覽器成為Web時代開啟時最著名的第一代互聯網公司,當時網景公司想在靜態頁面加一些動態效果,就讓Brendan Eich設計JS語言,這哥們真是一個天才,竟然在10天時間內寫出JS,沒錯,只有10天,至于為什么名字叫JavaScript,其實就是想蹭一下Java的熱度,語言本身與Java毫無關系,
ECMAScript
因為JavaScript的功能十分適合網頁的動態化,隨著計算機技術的不斷發展,人們也不再滿足于靜態網頁,微軟做為行業巨頭,自然也嗅到了這個機會,于是在JS問世后一年,微軟開發了Jscript,為了能夠讓JS成為全球標準,網景、微軟還有幾家公司聯合ECMA組織制定了Javascript語言標準,被稱為ECMAScript語言標準,
ECMAScript是一種語言標準,JavaScript是網景公司對ECMAScript標準的一種實作,
至于為什么不把JavaScript當作標準名稱呢,是因為JavaScript被網景注冊了商標,不是很嚴謹的場合可以把JS與ECMAScript當作一回事,
發展歷程,版本迭代
JS是10天被設計出來,雖然Eich很牛,但誰也架不住時間緊、任務重,所以,JS有很多設計缺陷,
2015年ES6標準發布
2 基礎
2.1 嵌入網頁
方式1
JS腳本可以被嵌入在網頁的各個部分,使用標簽,一般放在body或者head中
方式2 直接引入檔案
<script src="a.js"><script>
IDE 推薦
- VScode
- Sublime
- Notepad++
- HbuilderX
2.2 基本語法
JS每條陳述句以“;”結尾,陳述句塊用{,,,},JS并不強制要求在每個陳述句的結尾加“;”,解釋器負責給每個陳述句補上“;”,但是一般情況下,為了保持良好編程習慣,還是要在每行陳述句后面加上一個分號,
陳述句塊
var a = 10;
"hello world!";
var b = 123; var c = 456; // 這是兩條陳述句
注釋
// 單條
/*
多條注釋
*/
大小寫
JS大小寫敏感
2.3 資料型別
2.3.1 基本資料型別
Number
JS 不區分整數和浮點數,統一用Number表示
var a = 123;
console.log(typeof(a)); // number
123; // 整數123
0.456; // 浮點數0.456
1.2345e3; // 科學計數法表示1.2345x1000,等同于1234.5
-99; // 負數
NaN; // NaN表示Not a Number,當無法計算結果時用NaN表示
Infinity; // Infinity表示無限大,當數值超過了JavaScript的Number所能表示的最大值時,就表示為Infinity
數字型別可以使用+、-、*、/ 、%求余運算
字串
//''或者“”
// ``反引號可以包括多行文本
var s1 = "Hello";
var s2 = 'hello';
var s3 = `
人生苦短
我用JS
多行文本輸出
`;
布爾
true
false
布爾運算子多用于條件判斷,也可以進行邏輯運算,邏輯與&&,邏輯或||,邏輯非!
console.log(true && false); // false
console.log(true || false); // true
console.log(!true); // false
var age = 15;
if(age>18){
console.log("成年人");
}
else{
console.log("未成年人"); // 未成年人
}
JS中的相等
<script>
console.log(null === null); // true
console.log(NaN === NaN); // false
console.log(1/3 === (1-2/3)); // false
console.log(Math.abs(1/3)-Math.abs(1-(2/3))<0.0000001); // true
console.log('3'===3); // false
console.log('3'==3); // true
</script>
2.3.2 高級資料型別
陣列
JS 的陣列(Array)可以包含任意資料型別,并通過索引來訪問每個元素,通過length屬性獲取Array的長度
-
length
陣列內的元素是可以原地修改的,可以通過陣列的length屬性動態修改陣列的長度,
var arr = [1,-9,null,true,99.9]; console.log(arr.length); console.log(arr[1]); console.log(arr[2]); console.log(arr[4]); console.log(arr[5]); // 超過下標也不會報錯,返滬undefined arr.length = 8; arr[7] = 'hello'; console.log(arr); // [1, -9, null, true, 99.9, undefined, undefined , "hello"] -
indexOf
獲取陣列內元素的索引,如果沒有這個元素,回傳-1.如果有這個元素,直接回傳這個元素對應的索引數值
console.log(arr.indexOf(1)); //0 console.log(arr.indexOf(99.9)); //4 console.log(arr.indexOf('fa')); //-1 -
slice
截取陣列的部分元素,回傳一個新陣列,不包括結尾索引
var arr = [1,-9,null,true,99.9]; console.log(arr); //index.html:10 (5) [1, -9, null, true, 99.9] var arr_ = arr.slice(0,3); // 回傳一個新陣列 console.log(arr_); // (5) [1, -9, null, true, 99.9] console.log(arr); // (5) [1, -9, null, true, 99.9] -
push/pop
push:從陣列后面插入元素
pop:從資料后面洗掉元素
var arr = ["三國戰將"] arr.push("趙云"); arr.push("關羽"); arr.push("張飛"); console.log(arr); //(4) ["三國戰將", "趙云", "關羽", "張飛"] console.log(arr.pop()); // 洗掉最后一個元素并回傳 console.log(arr); // (3) ["三國戰將", "趙云", "關羽"] -
unshift/shift
unshift:從陣列頭部插入元素
shift:從資料頭部洗掉元素
var arr = ["三國戰將"] arr.unshift("趙云"); arr.unshift("關羽"); arr.unshift("張飛"); console.log(arr); //(4) ["張飛", "關羽", "趙云", "三國戰將"] console.log(arr.shift()); // 洗掉第一個元素并回傳 console.log(arr); // (3) ["關羽", "趙云", "三國戰將"] -
sort
對當前陣列進行排序,原地排序
var a = ["C","B","A"]; a.sort(); console.log(a); // (3) ["A", "B", "C"] -
reverse
反轉陣列,原地反轉陣列
var a = ["C","B","A"]; a.reverse(); console.log(a); // (3) ["A", "B", "C"] -
splice
splice()方法是修改Array的“萬能方法”,它可以從指定的索引開始洗掉若干元素,然后再從該位置添加若干元素:var a = ["喬丹","科比","艾弗森","卡特","詹姆斯"]; // 從第二個索引開始,洗掉兩個元素,回傳被洗掉的元素,同時從第二個索引開始添加新元素 var d = a.splice(2,2,"韋德","安東尼"); console.log(d); //(2) ["艾弗森", "卡特"] console.log(a); // (5) ["喬丹", "科比", "韋德", "安東尼", "詹姆斯"] -
concat
把當前陣列和另一個陣列鏈接起來,回傳一個新陣列
var a = ["C","B","A"]; var b = a.concat(1,2,3,[7,8,9]); console.log(a); // (3) ["C", "B", "A"] console.log(b); //(9) ["C", "B", "A", 1, 2, 3, 7, 8, 9] -
join
把陣列中的每個元素用指定的字串鏈接起來,回傳鏈接后的字串
var a = ["C","B","A"]; var b = a.join('-'); console.log(a); // (3) ["C", "B", "A"] console.log(b); //C-B-A -
多維陣列
var a = [[1,2,3],[4,5,6]]; console.log(a[1][1]); // 5 console.log(a[0][2]); // 3
物件
JS的物件是一種無序的集合資料型別,由若干鍵值對組成,物件是面向物件編程的基礎,程式中的一個個物件就是對現實生活的抽象,
用{,,,}表示一個物件, key:value形式申明,每個kv對之間用逗號分割,最后一個鍵值對不需要再末尾加,如果加了,有些老舊瀏覽器將報錯;通過點號可以點出一個物件的屬性,也可以通過obj[attr]獲取屬性,如果屬性名有特殊字符,那么屬性名必須用引號括起來,取屬性的時候不能使用點號,所以物件的屬性盡量使用沒有特殊符號的單詞,做到見名知意
var xiaoming = {
name:'小明',
birth:1994,
height:1.86,
weight:78.0,
score:null
}
console.log(xiaoming.name); // 小明
console.log(xiaoming.age); // undefined
console.log(xiaoming.birth); // 1994
console.log(xiaoming['weight']); // 78
xiaoming.xixi = 'haha';
console.log(xiaoming.xixi); // haha
- 即使物件沒有這個屬性,也不會報錯
- 可以創建物件后,動態設定屬性,這是動態語言所特殊的
console.log("xixi" in xiaoming); // true
console.log("toString" in xiaoming); // true
console.log(xiaoming.hasOwnProperty('xixi')); // true
console.log(xiaoming.hasOwnProperty('age')); // false
delete xiaoming.xixi;
console.log(xiaoming.hasOwnProperty('xixi')); // false
-
判斷物件是否具備某個屬性
可以使用in關鍵字,attr in object 回傳一個布林值,如上例,toString沒有在xiaoming這個物件中定義,但是同樣回傳true,這說明JS中所有物件的原型鏈都會有object物件,object物件有toString屬性;
如果只想拿到物件本身的屬性,可以使用object.hasOwnProperty()方法
Map
Map是一組鍵值對結果,Map的查找時間復雜度為O(1)
-
通過二維陣列構造一個Map
var m = new Map([['語文',78],['數學',98],['英語',79]]); console.log(m);//Map(3) {"語文" => 78, "數學" => 98, "英語" => 79} console.log(m.get('語文')); // 78 console.log(m.has('英語')); // true console.log(m.set('物理',100)); // Map(4) {"語文" => 78, "數學" => 98, "英語" => 79, "物理" => 100} delete m.delete('語文'); console.log(m);Map.get() 獲取一個key的值
Map.has()判斷這個集合中是否有這個key
Map.set() 設定key-value
Map.delete() 洗掉map的某個key
Set
Set和Map類似,Set是一個key的集合,但不存盤value,由于key不能重復,所以Set中每個key都是唯一的,
創建一個Set,需要提供Array作為輸入,或者直接創建一個空Set
var s = new Set();
s.add(1);
s.add(2);
s.add(3);
s.add(3);
console.log(s); // Set(3) {1, 2, 3}
var s1 = new Set([1,2,3,'3',3,3,3]);
console.log(s1); // Set(4) {1, 2, 3, "3"}
s1.delete('3');
s1.delete(2);
console.log(s1); // Set(2) {1, 3}
2.4 程式結構
2.4.1 順序結構
JS中的順序結構就是從上而下執行的,
2.4.2 選擇結構
把null、undefined、0、NaN和空字串''視為false,其他值一概視為true
'use strict';
var height = parseFloat(prompt("請輸入身高(m):"));
var weight = parseFloat(prompt("請輸入體重(kg):"));
var bmi = weight / (height*height);
if(bmi<18.5){
console.log("過輕");
}
else if(bmi>=18.5 && bmi < 25){
console.log("正常");
}
else if(bmi>=25 && bmi < 28){
console.log("過重");
}
else if(bmi >=28 && bmi < 32){
console.log("肥胖");
}
else if(bmi >=32){
console.log("嚴重肥胖");
}
else{
console.log("資料有誤");
}
2.4.3 回圈結構
-
while
先判斷條件,條件不滿足一次都不執行
'use strict'; var sum = 0; var i =0 ; while(i<=100){ sum += i; i ++ } console.log(sum); // 5050 -
do-while
先執行回圈體,在判斷條件,至少會執行一次回圈體
'use strict'; var sum = 0; do{ sum += i; i ++ }while(i<=100); console.log(sum);通過一個小實體看一下while與do… while的區別
'use strict'; var sum = 0; var sum_ = 0; var i =1 ; do{ sum += i; i ++ }while(i>100); console.log(sum); // 1 while(i>100){ sum_ += i; i++; } console.log(sum_); // 0回圈一定要注意結束條件,< 與 <= 會是完全不同的結果
-
普通for回圈
與C語言的for回圈基本相同
var sum = 0; for(let i=0;i<=100;i++){ sum += i; } console.log(sum); // 5050 -
for…in
遍歷陣列和物件這種容器資料型別
'use strict'; var arr = ["張飛","關羽", "趙云"]; for(let i in arr){ console.log(i); console.log(arr[i]); } var obj1 = { name:'xiaohua', age:14, grade:4 } for(let i in obj1){ console.log(i); console.log(obj1[i]); }

-
for…of
遍歷
Array可以采用下標回圈,遍歷Map和Set就無法使用下標,為了統一集合型別,ES6標準引入了新的iterable型別,Array、Map和Set都屬于iterable型別,具有
iterable型別的集合可以通過新的for ... of回圈來遍歷,'use strict'; var arr = ["張飛","關羽", "趙云"]; for(let i of arr){ console.log(i); } for(let i of new Map([['語文',12],['數學',78]])){ console.log(i); } for(let i of new Set([1,2,3])){ console.log(i); }

-
foreach
iterable內置的forEach方法,它接收一個函式,每次迭代就自動回呼該函式,'use strict'; var a = ['A', 'B', 'C']; a.forEach(function (element, index, array) { // element: 指向當前元素的值 // index: 指向當前索引 // array: 指向Array物件本身 console.log(element + ', index = ' + index); }); // 張飛, index = 0 // 關羽, index = 1 // 趙云, index = 2
3 函式
函式是對一系列動作的抽象
3.1 函式定義
3.1.1 定義函式的兩種方式
方式1:
'use strict';
function abs(x) {
if (x >= 0) {
return x;
} else {
return -x;
}
}
var result = abs(-9);
console.log(result);
方式2
? 函式也是一個物件,通過function()關鍵字定義函式
'use strict';
var abs = function (x) {
if (x >= 0) {
return x;
} else {
return -x;
}
}
var result = abs(-9);
console.log(result);
3.1.2 函式引數
JS函式對于引數沒有限制,可以多傳遞,也可以少傳遞,都不會報錯
'use strict';
var abs = function (x) {
if (x >= 0) {
return x;
} else {
return -x;
}
}
var result = abs();
console.log(result); // NaN
result = abs(-9,1,'b','c','d','w');
console.log(result); // 9
arguments
arguments 是JS的一個關鍵字,這個關鍵字只有在函式內部有用,存盤的資訊是傳入函式的引數,arguments類似Array但它不是一個Array,
'use strict';
var abs = function (x) {
console.log(arguments);
for (let i=0; i<arguments.length;i++){
console.log(arguments[i]);
}
if (x >= 0) {
return x;
} else {
return -x;
}
}
var result;
result = abs(-9,1,'b','c','d','w');
console.log(result); // 9

通過實體可以發現,無論有沒有給函式加形式引數,都可以通過arguments關鍵字獲取,
rest 引數
ES6標準引入了rest引數,arguments獲取到了所有的輸入引數,rest引數只獲取額外的引數,rest引數前面使用…標識,
var test = function (a, b, ...rest){
console.log(a); // 1
console.log(b); // 2
console.log(rest); // (4) [3, 4, 5, 6]
}
test(1,2,3,4,5,6);
小心return
var test = function (a, b, ...rest){
console.log(a); // 1
console.log(b); // 2
console.log(rest); // (4) [3, 4, 5, 6]
return //瀏覽器會默認添加一個分號
10 // 這句話就執行不了了
}
var res = test(1,2,3,4,5,6);
console.log(res); // undefined
3.2 變數作用域
如果一個變數在函式體內部申明,則該變數的作用域為整個函式體,在函式體外不能應用改變數
'use strict';
var f1 = function(){
var a = 10;
console.log(a);
}
console.log(a); //main.js:9 Uncaught ReferenceError: a is not defined at main.js:9
不同函式的相同函式名是相互獨立,互不干擾的
嵌套函式,內部函式可以訪問外部函式的作用域,外部函式訪問不到內部函式的作用域
'use strict';
function outer(){
var a = 10;
function inner(){
var b = 11;
console.log(a);
}
inner(); // 10
console.log(b); // ReferenceError: b is not defined
}
outer();
內外部函式具有相同名稱的變數,內部函式會覆寫外部函式
function outer(){
var a = 10;
function inner(){
var a = 11;
console.log(a);
}
inner(); // 11
console.log(b); // ReferenceError: b is not defined
}
outer();
這說明JavaScript的函式在查找變數時從自身函式定義開始,從“內”向“外”查找,如果內部函式定義了與外部函式重名的變數,則內部函式的變數將“屏蔽”外部函式的變數,
3.3 變數提升
JS有三種宣告變數的關鍵字,const, let, var
const 定義常量
'use strict';
const PI = 3.14;
PI = 3; // TypeError: Assignment to constant variable.
console.log(PI);
// 不能改變常量的值
// 具有塊級作用域
{
const sum = 0
const a = 11;
console.log(sum); // 0
}
{
const sum =100;
const a = 12;
console.log(sum); // 110
}
console.log(sum); // main.js:18 Uncaught ReferenceError: sum is not defined
let 定義變數具有塊級作用域,沒有變數提升
// let不具備變數提升
var f1 = function(){
var a = 1;
console.log(a+b); // ReferenceError: b is not defined
let b =2;
}
f1()
// 具有塊級作用域
{
let sum = 0
let a = 11;
sum += a;
console.log(sum); // 11
}
{
let sum =100;
let a = 12;
sum += a;
console.log(sum); // 112
}
console.log(sum); // main.js:18 Uncaught ReferenceError: sum is not defined
vat 定義變數不具備塊級作用域,具有變數提升
// 未定義變數b,直接使用不報錯,是因為JS變數提升機制,但是賦值不會提升
var f1 = function(){
var a = 1;
console.log(a+b); // NaN
var b =2;
}
f1()
// 沒有塊級作用域
{
var sum = 0
var a = 11;
sum += a;
console.log(sum); // 11
}
{
var sum =100;
var a = 12;
sum += a;
console.log(sum); // 112
}
console.log(sum); // 112
3.4 全域作用域
不在任何函式內定義的變數就具有全域作用域,實際上,JavaScript默認有一個全域物件window,全域作用域的變數實際上被系結到window的一個屬性
var sub = "learn";
console.log(sub); // learn
console.log(window.sub); // learn
alert("hello");
window.alert('hello');
// 全域window
var temp = window.alert;
window.alert = function(){}
alert("hello"); // 沒反應
window.alert = temp;
alert("hello"); // 觸發
3.5 命名空間
使用命令空間可以凈化全域作用域
var myApp = {}
myApp.test = "hello"
myApp.f1 = function() {
console.log("myapp f1 is running");
}
myApp.f1();
var myApp2 = {}
myApp2.test = "world";
myApp2.f1 = function() {
console.log("myapp f2 is running");
}
myApp2.f1();
3.6 解構賦值
// 1 直接拆包
var [x,y,z] = ['hello','china','haha'];
console.log(x); // hello
console.log(y); // china
console.log(z); // haha
// 2 格式必須相同
var [x,y,[a,b]] = ['中國','河北',['北京','天津']];
console.log(x); // 中國
console.log(y); // 河北
console.log(a); // 北京
console.log(b); // 天津
// 3 可以忽略一部分
var [,,x] = ['hello','china','haha'];
console.log(x); // haha
3.7 方法
在一個物件中系結函式,叫做這個物件得方法
var xiaoming = {
name: '小明',
birth: 1990,
age:function(){
var y = new Date().getFullYear();
return y-this.birth;
},
};
var age = xiaoming.age();
console.log(age);
this關鍵字
this是一個特殊的關鍵字,始終指向當前物件,也就是上例中的xiaoming物件,所以,this.birth可以拿到xiaoming的birth屬性,
// 拆開寫
3.8 高階函式
3.8.1 map/reduce
map 將一個函式作用在一個陣列的所有元素上
function pow(x){
return x*x;
}
var arr = [1,2,3,4,5,6,7,8,9];
var new_arr = arr.map(pow);
console.log(arr); // (9) [1, 2, 3, 4, 5, 6, 7, 8, 9]
console.log(new_arr); // (9) [1, 4, 9, 16, 25, 36, 49, 64, 81]
// 同樣可以用for回圈實作
var new_arr = new Array();
for(let i=0; i<arr.length;i++){
new_arr.push(pow(arr[i]));
}
console.log(new_arr); // (9) [1, 4, 9, 16, 25, 36, 49, 64, 81]
reduce
function add(x,y){
return x+y;
}
var arr = [1,2,3,4,5,6,7,8,9];
var res = arr.reduce(add);// = add(add(add(add(add(add(add(add(1,2),3),4),5),6),7,8,9)
console.log(res);
3.8.2 filter
對一個陣列內的元素進行過濾,滿足條件的保留,不滿足的剔除
'use strict';
var arr = [1,2,3,4,5,6,7,8,9];
function even(x){
if ((x%2)==0){
return true;
}
else{
return false;
}
}
var res = arr.filter(even);
console.log(res); // (4) [2, 4, 6, 8]
3.8.3 sort
var arr = [10, 20, 1, 2];
arr.sort((x, y) => {
return x - y
});
console.log(arr); // [1, 2, 10, 20]
4 面向物件
4.1 JS創建物件的方式
字面量
'use strict';
var obj = {
"name":"zhangsan",
"age":14,
"grade":"一年級"
};
console.log(obj);
使用建構式創建物件
function Player(name, age){ // 這是一個建構式
this.name = name;
this.age = age;
this.run = function() {
console.log(this.name + " is running!");
}
}
var kobe = new Player("kobe", 12);
kobe.run(); // kobe is running!
var james = new Player("James", 123);
james.run();//James is running!
如果不寫new,這就是一個普通函式,它回傳undefined,但是,如果寫了new,它就變成了一個建構式,它系結的this指向新創建的物件,并默認回傳this,也就是說,不需要在最后寫return this;
忘記寫new的后果
-
‘use strict’; 模式下,this的指向是undefined,給一個undefined系結name會報錯
'use strict'; function Player(name, age){ this.name = name; // Cannot set properties of undefined (setting 'name') this.age = age; this.run = function() { console.log(this.name + " is running!"); } } var kobe = Player("kobe", 12); // main.js:5 Uncaught TypeError: Cannot set properties of undefined (setting 'name') -
不是嚴格模式下,this指向的是window,this.name會直接設定一個全域變數,這樣更危險,因為污染了命名空間
function Player(name, age){ this.name = name; this.age = age; this.run = function() { console.log(this.name + " is running!"); } } Player("kobe", 11); console.log(name); //kobe console.log(age); // 11 run(); // kobe is running!
4.2 原型物件(prototype)和物件原型(_ _ proto _ _)
JS 中一切皆物件,每個物件都會設定一個原型,也就是物件原型_ _ proto _ _屬性,物件原型這個屬性指向它的原型物件,這里說起來比較繞,原型鏈也是JS中的重點和難點,原型鏈在JS中的地位就類似Python中魔法方法的地位,搞不懂就只能停留到初級階段,無法體會JS的精髓,
使用建構式創建物件存在的問題

'use strict';
function Player(name, age){
this.name = name;
this.age = age;
this.run = function() {
console.log(this.name + " is running!");
}
}
var kobe = new Player("kobe",456);
var james = new Player("James", 123);
console.log(kobe.run == james.run); // false
通過上面的圖片和代碼的運行結果可以發現,每創建一個物件都會在記憶體中開辟出一個空間,保存這個物件的屬性和方法,屬性沒有關系,但是相同的方法卻被復制了很多次,如果創建的物件很多,那么程式的時間復雜度和空間復雜度會成倍增加,顯然這不是一個好辦法,
JS的發明者自然考慮到了這個問題,可以通過原型物件來解決,前面已經說過了JS中的每個物件都有一個原型物件,我們看一下什么是原型物件:

'use strict';
function Player(name, age){
this.name = name;
this.age = age;
}
Player.prototype.run = function(){
console.log(this.name + "is running");
}
var kobe = new Player("kobe",456);
var james = new Player("James", 123);
console.log(kobe);
console.log(kobe.run == james.run); // false
![[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-odeYPvd4-1636016576605)(C:\Users\z004abwh\AppData\Roaming\Typora\typora-user-images\image-20211104151108745.png)]](https://img.uj5u.com/2021/11/06/281545060847213.png)
物件的原型鏈
kobe------> Player.prototype ----------> Object.prototype ----------> null
4.3 原型物件(prototype)中的constructor屬性
原型物件的constructor屬性指向的是建構式本身,
console.log(kobe.__proto__.constructor); // 指向的就是其建構式
/*
? Player(name, age){
this.name = name;
this.age = age;
}
*/
constructor用法:
如果一個建構式,類似下面這種方式網原型物件添加屬性,寫起來會很長,之間的關系不是很清晰
function Player(name, age){
this.name = name;
this.age = age;
}
Player.prototype.run = function(){
console.log(this.name + "is running");
}
Player.prototype.jump = function(){
console.log(this.name + "is jumpping");
}
換一種方式:
function Player(name, age){
this.name = name;
this.age = age;
}
Player.prototype = {
constructor: Player, // 如果不加這個原型物件就會被覆寫,也就不知道實體是由哪個類實體化出來的
run: function(){
console.log(this.name + "is running");
},
jump:function(){
console.log(this.name + "is jumpping");
}
}
var kobe = new Player("kobe",456);
var james = new Player("James", 123);
console.log(kobe.__proto__.constructor);
console.log(kobe.__proto__)
console.log(kobe instanceof Player);
![[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-PFHCdLPf-1636016576606)(C:\Users\z004abwh\AppData\Roaming\Typora\typora-user-images\image-20211104152436251.png)]](https://img.uj5u.com/2021/11/06/281545060847218.png)
如果把constructor: Player這句話注釋掉,結果如下:

![[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-Q8G8KMaM-1636016576608)(C:\Users\z004abwh\AppData\Roaming\Typora\typora-user-images\image-20211104163927922.png)]](https://img.uj5u.com/2021/11/06/281545060847219.png)
4.4 原型鏈實作繼承
4.4.1 call方法
function add(x, y){
console.log(x+y);
}
function sub(x, y){
console.log(x-y);
}
add.call(sub,1,3); // 4 用add函式替代sub函式
sub.call(add,1,3); // -2 用sub函式替代add函式
4.4.2 使用call繼承
function Sex(props){
this.sex = props.sex;
}
function Person(props){
this.name = props.name;
}
function Student(props){
Person.call(this, props);
Sex.call(this, props);
this.grade = props.grade;
}
var s1 = new Student({
name:'zs',
grade:123,
sex:'男'
});
console.log(s1.name); // zs
console.log(s1.grade); // 123
console.log(s1.sex); // 男
4.4.3 原型鏈詳解
function Person(props){
this.name = props.name;
}
Person.prototype.say = function(){
console.log("人都會說話的");
}
function Student(props){
Person.call(this, props);
this.grade = props.grade;
}
var s1 = new Student({
name:'zs',
grade:123
});
console.log(s1);
s1.say(); // TypeError: s1.say is not a function
console.log(s1.__proto__ == Student.prototype) // true
console.log(s1.__proto__.__proto__ == Object.prototype) // true
console.log(s1.__proto__.__proto__.__proto__ == null) // true
為什么出現這種報錯呢?
s1 ------> Student.prototype -------> Object.prototype --------> null
這是什么鳥繼承?只是繼承了屬性,但是原型鏈沒有經過Person.prototype,所以會報錯,
正確的原型鏈應該是這樣的:
s1 ------> Student.prototype -------> Person.prototype ------> Object.prototype --------> null
Student.prototype = Person.prototype這樣是不行的,因為Student和Person共享一個原型物件,那么創建兩個類也沒有意義,
4.4.4 通過“中間人”鏈接原型鏈
function Person(props){
this.name = props.name;
}
Person.prototype.say = function(){
console.log("人都會說話的");
}
function Mid(){}
function Student(props){
Person.call(this, props);
this.grade = props.grade;
}
Mid.prototype = Person.prototype; // 把Mid的原型指向 Person.prototype
Student.prototype = new Mid() ; // 把Student的原型指向一個Mid物件, Mid物件的原型正好指向Person.prototype
Student.prototype.constructor = Student; // 把Student的原型的建構式恢復成Student
var s1 = new Student({
name:'zs',
grade:123
});
console.log(s1);
s1.say(); //人都會說話的
console.log(s1.__proto__ == Student.prototype) // true
console.log(s1.__proto__.__proto__ == Person.prototype) // true
console.log(s1.__proto__.__proto__.__proto__ == Object.prototype) // true
![[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-xVWGsjwA-1636016576608)(C:\Users\z004abwh\AppData\Roaming\Typora\typora-user-images\image-20211104165043702.png)]](https://img.uj5u.com/2021/11/06/2815450608472110.png)
4.5 class語法糖
使用class創建類
'use strict';
class Person {
constructor (name){
this.name = name;
}
say() {
console.log(this.name + " is saying !");
}
}
var p1 = new Person("正常人");
p1.say() // 正常人 is saying !
4.5.1 class實作繼承
ES6的新特性通過class實作繼承,class繼承的方式可以減少撰寫原型鏈的代碼,但是前提是瀏覽器要支持ES6.
'use strict';
class Person {
constructor (name){
this.name = name;
}
say() {
console.log(this.name + " is saying !");
}
}
class Student extends Person{
constructor(name,grade){
super(name);
this.grade = grade;
}
study() {
console.log(this.grade+"年級的學生就應該學習");
}
}
var p1 = new Student("學生甲乙丙丁", 4);
p1.say(); //學生甲乙丙丁 is saying !
p1.study(); // 4年級的學生就應該學習
轉載請註明出處,本文鏈接:https://www.uj5u.com/qianduan/349656.html
標籤:其他
上一篇:簡單的 html拖動模態框 案例
