ES6中定義類.繼承以及對象的操作0712
1.定義類的不同
回顧一下以前學習的在ES6之前定義一個類(不明顯,和普通的函數(shù)區(qū)別不大)
function Person(myName, myAge) { // 實例屬性 // this.name = "lnj"; // this.age = 34; this.name = myName; this.age = myAge; // 實例方法 this.say = function () { console.log(this.name, this.age); } // 靜態(tài)屬性 Person.num = 666; // 靜態(tài)方法 Person.run = function () { console.log("run"); } } // let p = new Person(); let p = new Person("zs", 18); p.say(); console.log(Person.num); Person.run();
從ES6開始系統(tǒng)提供了一個名稱叫做class的關(guān)鍵字, 這個關(guān)鍵字就是專門用于定義類的(以后就這樣寫)
注意里面的注意點哦
class Person{ // 當我們通過new創(chuàng)建對象的時候, 系統(tǒng)會自動調(diào)用constructor // constructor我們稱之為構(gòu)造函數(shù) constructor(myName, myAge){ this.name = myName; this.age = myAge; } // 注意點:實例屬性,不是正式寫法,大部分瀏覽器不支持,所以我們還是寫在constructor里好 // name = "lnj"; // age = 34; // 注意點:實例方法,如果寫在constructor外面,系統(tǒng)會將這個方法添加到原型對象上面,想添加在實例對象上還是得寫在里面,所以定義在constructor外的方法就相當于以前定義在原型對象上的方法 say(){ console.log(this.name, this.age); } // 注意點:以下定義"靜態(tài)屬性"的方式并不是ES6正式版標準中的寫法, 大部分的瀏覽器不支持 // 在ES標準中static只支持定義靜態(tài)方法不支持定義靜態(tài)變量,所以想要定義靜態(tài)屬性,應(yīng)定義在類外 // 靜態(tài)屬性 static num = 666; // 靜態(tài)方法 static run() { console.log("run"); } } //以下自定義一個原型對象在ES6中不可用,如果想將屬性和方法保存到原型中, 只能動態(tài)給原型對象添加屬性和方法 let obj = { constructor: Person, type: "人", say: function () { console.log(this.name, this.age); } }; let p = new Person("zs", 18); p.say(); console.log(Person.num); Person.run();
綜上所述,我們來寫一個最標準的類的定義
class Person{ constructor(myName, myAge){ //方法和屬性都寫在里面 this.name = myName; this.age = myAge; this.say = function(){ console.log(this.name, this.age); } } //只能寫靜態(tài)方法 static run() { console.log("run"); } } //動態(tài)給原型對象添加方法和屬性 Person.prototype.type = "人"; Person.prototype.say = function () { console.log(this.name, this.age); }; //靜態(tài)實行需要類外定義 Person.num = 666; let p = new Person("zs", 18); p.say(); console.log(Person.name); Person.run();
2.繼承的不同
在ES6中如何繼承:在子類后面添加extends并指定父類的名稱,在子類的constructor構(gòu)造函數(shù)中通過super方法借助父類的構(gòu)造函數(shù)
class Person{ constructor(myName, myAge){ // this = stu; this.name = myName; // stu.name = myName; this.age = myAge; // stu.age = myAge; } say(){ console.log(this.name, this.age); } } // 以下代碼的含義: 告訴瀏覽器將來Student這個類需要繼承于Person這個類 class Student extends Person{ constructor(myName, myAge, myScore){ // 1.在子類中通過call/apply方法借助父類的構(gòu)造函數(shù) // Person.call(this, myName, myAge);相當于下面那條代碼 super(myName, myAge); this.score = myScore; } study(){ console.log("day day up"); } } let stu = new Student("zs", 18, 98); stu.say();
3.獲取對象類型
現(xiàn)在我們有如下需求,獲取各自對應(yīng)的類型
/* let obj = new Object(); --> object let arr = new Array(); --> Array let p = new Person(); --> Person */ console.log(typeof obj); console.log(typeof arr); console.log(typeof p);
但是通過typeof獲取的類型都是object(理解一下),那怎么滿足我們的需求呢.看以下的
function Person() { // let obj = new Object(); // let this = obj; this.name = "lnj"; this.age = 34; this.say = function () { console.log(this.name, this.age); } // return this; } let p = new Person(); // console.log(typeof p); // object console.log(p.constructor.name); // Person
為什么p.constructor.name 能獲得是什么類型呢,如下我們調(diào)用arr.constructor,就從原型對象找見了,然后指向了構(gòu)造函數(shù),構(gòu)造函數(shù)里面有一個name屬性就能得到是啥類型
[圖片上傳失敗...(image-692cc1-1562946030563)]
4. instanceOf關(guān)鍵字
什么是instanceof關(guān)鍵字?
instanceof用于判斷 "對象" 是否是指定構(gòu)造函數(shù)的 "實例"
注意點: 只要構(gòu)造函數(shù)的原型對象出現(xiàn)在實例對象的原型鏈中都會返回true
例如下面的例子
function Person(myName) { this.name = myName; } function Student(myName, myScore) { Person.call(this, myName); this.score = myScore; } Student.prototype = new Person(); Student.prototype.constructor = Student; let stu = new Student(); console.log(stu instanceof Person); // true 這就說明了上面的觀點 console.log(stu instanceof Student);
5.isPrototypeOf屬性
什么是isPrototypeOf屬性
isPrototypeOf用于判斷 一個對象是否是另一個對象的原型 (前面的是不是后面的)
注意點: 只要調(diào)用者在傳入對象的原型鏈上都會返回true
function Person(myName) { this.name = myName; } function Student(myName, myScore) { Person.call(this, myName); this.score = myScore; } Student.prototype = new Person(); Student.prototype.constructor = Student; let stu = new Student(); console.log(Person.prototype.isPrototypeOf(stu)); // true
6.判斷對象是否有某個屬性
6.1判斷某一個對象是否擁有某一個屬性
- "屬性名" in 對象名
- in的特點: 只要類中或者原型對象中有, 就會返回true
6.2 判斷某一個對象自身是否擁有某一個屬性
- 對象名.hasOwnProperty("屬性名");
- 特點: 只會去類中查找有沒有, 不會去原型對象中查找
7.對象的增刪改查
class Person{} let p = new Person();
7.1增
- p.name = "lnj";
- p["name"] = "zs";
7.2 刪
- delete p.name;
- delete p["name"];
7.3 改
- p.name = "lnj"; 覆蓋就行
8.對象遍歷
在JavaScript中對象和數(shù)組一樣是可以遍歷的
- 對象的遍歷就是依次取出對象中所有的屬性和方法
在JS中可以通過高級for循環(huán)來遍歷對
for(let key in obj){}
將指定對象中所有的屬性和方法的名稱取出來了依次的賦值給key這個變量
for(let key in p){ if(p[key] instanceof Function){ continue; } // console.log(key); // name / age / say // 注意點: 以下代碼的含義取出p對象中名稱叫做當前遍歷到的名稱的屬性或者方法的取值 console.log(p[key]); // p["name"] / p["age"] / p["say"] // 注意點: 以下代碼的含義取出p對象中名稱叫做key的屬性的取值 // console.log(p.key); // undefined }
10.深拷貝與淺拷貝
10.1深拷貝
- 修改新變量的值不會影響原有變量的值
- 默認情況下基本數(shù)據(jù)類型都是深拷貝
10.2淺拷貝
修改新變量的值會影響原有的變量的值
默認情況下引用類型都是淺拷貝
class Person{ name = "lnj"; age = 34; } let p1 = new Person(); let p2 = p1; p2.name = "zs"; // 修改變量的值 console.log(p1.name); //變?yōu)閦s
原理如下
10.3 對象深拷貝
以下兩種只能拷貝基本數(shù)據(jù)類型
- 1.可以通過在新建一個對象,然后通過for循環(huán)遍歷來實現(xiàn)拷貝(low)
- 2.Object.assign(p2,p1); 將p1中屬性和方法拷貝到p2中
在看一下有引用類型的
class Person{ name = "lnj"; cat = { age : 3 }; scores = [1, 3, 5]; } let p1 = new Person(); let p2 = new Object(); p2.cat = p1.cat;
執(zhí)行完上面代碼后會發(fā)生什么
這不就出事了嗎
所以我們需要自定義一個函數(shù)來實現(xiàn)深拷貝,圖示如下
function depCopy(target, source) { // 1.通過遍歷拿到source中所有的屬性 for(let key in source){ // console.log(key); // 2.取出當前遍歷到的屬性對應(yīng)的取值 let sourceValue = source[key]; // console.log(sourceValue); // 3.判斷當前的取值是否是引用數(shù)據(jù)類型 if(sourceValue instanceof Object){ // console.log(sourceValue.constructor); // console.log(new sourceValue.constructor); //這樣如果是object就會創(chuàng)建一個object類型,如果是array類型就會創(chuàng)建一個array類型 let subTarget = new sourceValue.constructor; target[key] = subTarget; //遞歸調(diào)用實現(xiàn)拷貝, depCopy(subTarget, sourceValue); }else{ target[key] = sourceValue; } } }
11.數(shù)組高級API
11.1 遍歷對象 forin
forin用來遍歷對象,但是對象的屬性是無序的, 所以forin循環(huán)就是專門用于遍歷無序的東西的, 所以不推薦使用forin循環(huán)來遍歷數(shù)組
for(let key in obj){ console.log(obj[key]); }
11.2 遍歷數(shù)組
-
利用Array對象的forEach方法來遍歷數(shù)組
forEach方法會自動調(diào)用傳入的函數(shù)
每次調(diào)用都會將當前遍歷到的元素和當前遍歷到的索引和當前被遍歷的數(shù)組傳遞給這個函數(shù)
arr.forEach(function (currentValue, currentIndex, currentArray) { // console.log(currentValue, currentIndex, currentArray); console.log(currentValue); }); //自己來實現(xiàn)一個 Array.prototype.myForEach = function (fn) { // this === [1, 3, 5, 7, 9] for(let i = 0; i < this.length; i++){ fn(this[i], i, this); } }; arr.myForEach(function (currentValue, currentIndex, currentArray) { console.log(currentValue, currentIndex, currentArray); });
-
利用ES6中推出的for of循環(huán)來遍歷數(shù)組
- for(let value of arr)
11.3數(shù)組的findIndex方法
findIndex方法: 定制版的indexOf, 找到返回索引, 找不到返回-1
let arr = [3, 2, 6, 7, 6]; let index = arr.findIndex(function (currentValue, currentIndex, currentArray) { // console.log(currentValue, currentIndex, currentArray); // if(currentValue === 6){ if(currentValue === 10){ return true; } }); //自己實現(xiàn)了一個 rray.prototype.MyfindIndex = function (fn) { // this === [1, 3, 5, 7, 9] for(let i = 0; i < this.length; i++){ let re = fn(this[i], i, this); if (re === true){ return i; } if(i == this.length-1) return -1; } };
11.4 數(shù)組的find方法
find方法返回索引, find方法返回找到的元素,find方法如果找到了就返回找到的元素, 如果找不到就返回undefined
let value = arr.find(function (currentValue, currentIndex, currentArray) { // console.log(currentValue, currentIndex, currentArray); // if(currentValue === 6){ if(currentValue === 10){ return true; } });
11.5 數(shù)組的filter方法
將滿足條件的元素添加到一個新的數(shù)組中
let newArray = arr.filter(function (currentValue, currentIndex, currentArray) { // console.log(currentValue, currentIndex, currentArray); if(currentValue % 2 === 0){ //要滿足的條件 return true; } }); console.log(newArray); // [2, 4] //自己實現(xiàn)一下 Array.prototype.myFilter = function (fn) { let newArray = []; for(let i = 0; i < this.length; i++){ let result = fn(this[i], i, this); if(result){ newArray.push(this[i]); } } return newArray; }
11.6 數(shù)組的map方法(和filter的區(qū)別看一下)
將滿足條件的元素映射到一個新的數(shù)組中(新數(shù)組的長度與原數(shù)組一樣,沒有就返回undefined)
let newArray = arr.map(function (currentValue, currentIndex, currentArray) { // console.log(currentValue, currentIndex, currentArray); if(currentValue % 2 === 0){ return currentValue; } }); console.log(newArray); // [undefined, 2, undefined, 4, undefined] //自己實現(xiàn) Array.prototype.myMap = function (fn) { let newArray = new Array(this.length); newArray.fill(undefined); for(let i = 0; i < this.length; i++){ let result = fn(this[i], i, this); if(result !== undefined){ newArray[i] = result; } } return newArray; }
11.7 刪除數(shù)組元素注意點
let len = arr.length; for(let i = 0; i < arr.length; i++){ arr.splice(i,1); } //刪不干凈,因為每刪一次,數(shù)組中的元素會往前移,導(dǎo)致最后沒法刪了 //解決辦法,從后往前刪 for(let i = len - 1; i >= 0; i--){ // console.log(arr.length); // 5, 4, 3 // console.log(len); arr.splice(i, 1); } //解決辦法二,采用delete for(let i = 0; i < arr.length; i++){ console.log(arr.length); // 注意點: 通過delete來刪除數(shù)組中的元素, 數(shù)組的length屬性不會發(fā)生變化 delete arr[i]; }
11.8 數(shù)組排序
// 如果元素是字符串類型, 那么比較的是字符串的Unicode編碼 let arr = ["c", "a", "b"]; arr.sort(function (a, b) { if(a > b){ return -1; }else if(a < b){ return 1; }else{ return 0; } }); //如果數(shù)組中的元素是數(shù)值類型 //如果需要升序排序, 那么就返回a - b; //如果需要降序排序, 那么就返回b - a; arr.sort(function (a, b) { return b - a; }); //按長度排序 let arr = ["1234", "21", "54321", "123", "6"]; arr.sort(function (str1, str2) { // return str1.length - str2.length; 短的排前面 return str2.length - str1.length; }); //對象數(shù)組的排序 let students = [ {name: "zs", age: 34}, {name: "ls", age: 18}, {name: "ww", age: 22}, {name: "mm", age: 28}, ]; students.sort(function (o1, o2) { // return o1.age - o2.age; return o2.age - o1.age; });
12.字符串操作
在js中字符串可以看做一個特殊的數(shù)組, 所以大部分數(shù)組的屬性/方法字符串都可以使用
// 1.獲取字符串長度 .length let str = "abcd"; console.log(str.length); // 2.獲取某個字符 [索引] / charAt let str = "abcd"; let ch = str[1];//高級瀏覽器才支持 let ch = str.charAt(1); console.log(ch); // 3.字符串查找 indexOf / lastIndexOf / includes let str = "vavcd"; let index = str.indexOf("v"); let index = str.lastIndexOf("v"); console.log(index); let result = str.includes("p"); console.log(result); //返回true和false // 4.拼接字符串 concat / + let str1 = "www"; let str2 = "it666"; let str = str1 + str2; // 推薦 let str = str1.concat(str2); console.log(str); // 5.截取子串 slice / substring / substr let str = "abcdef"; let subStr = str.slice(1, 3); let subStr = str.substring(1, 3); let subStr = str.substr(1, 3); console.log(subStr); // 6.字符串切割 let arr = [1, 3, 5]; let str = arr.join("-");//將數(shù)組換成一個字符串,用-鏈接 console.log(str); let str = "1-3-5"; let arr = str.split("-");//切割 console.log(arr); // 7.判斷是否以指定字符串開頭 ES6 let str = "http://www.it666.com"; let result = str.startsWith("www"); console.log(result); // 8.判斷是否以指定字符串結(jié)尾 ES6 let str = "lnj.jpg"; let result = str.endsWith("png"); console.log(result); // 4.字符串模板 ES6 let str = ""; 以前定義字符串 let str = ''; let str = `www.it666.com`;//新增定義字符串 console.log(str); console.log(typeof str); //以前得通過+拼接 let str = "<ul>\n" + " <li>我是第1個li</li>\n" + " <li>我是第2個li</li>\n" + " <li>我是第3個li</li>\n" + "</ul>"; //現(xiàn)在直接寫在``中就行 let str = `<ul> <li>我是第1個li</li> <li>我是第2個li</li> <li>我是第3個li</li> </ul>`; let name = "lnj"; let age = 34; //以前的拼接方法 let str = "我的名字是" + name + ",我的年齡是" + age; //通過``拼接 let str = `我的名字是${name},我的年齡是${age}`; console.log(str);