ES6中定义类.继承以及对象的操作0712
1.定义类的不同
回顾一下以前学习的在ES6之前定义一个类(不明显,和普通的函数区别不大)
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); } // 静态属性 Person.num = 666; // 静态方法 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开始系统提供了一个名称叫做class的关键字, 这个关键字就是专门用于定义类的(以后就这样写)
注意里面的注意点哦
class Person{ // 当我们通过new创建对象的时候, 系统会自动调用constructor // constructor我们称之为构造函数 constructor(myName, myAge){ this.name = myName; this.age = myAge; } // 注意点:实例属性,不是正式写法,大部分浏览器不支持,所以我们还是写在constructor里好 // name = "lnj"; // age = 34; // 注意点:实例方法,如果写在constructor外面,系统会将这个方法添加到原型对象上面,想添加在实例对象上还是得写在里面,所以定义在constructor外的方法就相当于以前定义在原型对象上的方法 say(){ console.log(this.name, this.age); } // 注意点:以下定义"静态属性"的方式并不是ES6正式版标准中的写法, 大部分的浏览器不支持 // 在ES标准中static只支持定义静态方法不支持定义静态变量,所以想要定义静态属性,应定义在类外 // 静态属性 static num = 666; // 静态方法 static run() { console.log("run"); } } //以下自定义一个原型对象在ES6中不可用,如果想将属性和方法保存到原型中, 只能动态给原型对象添加属性和方法 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); } } //只能写静态方法 static run() { console.log("run"); } } //动态给原型对象添加方法和属性 Person.prototype.type = "人"; Person.prototype.say = function () { console.log(this.name, this.age); }; //静态实行需要类外定义 Person.num = 666; let p = new Person("zs", 18); p.say(); console.log(Person.name); Person.run();
2.继承的不同
在ES6中如何继承:在子类后面添加extends并指定父类的名称,在子类的constructor构造函数中通过super方法借助父类的构造函数
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方法借助父类的构造函数 // 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.获取对象类型
现在我们有如下需求,获取各自对应的类型
/* 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 能获得是什么类型呢,如下我们调用arr.constructor,就从原型对象找见了,然后指向了构造函数,构造函数里面有一个name属性就能得到是啥类型
[图片上传失败...(image-692cc1-1562946030563)]
4. instanceOf关键字
什么是instanceof关键字?
instanceof用于判断 "对象" 是否是指定构造函数的 "实例"
注意点: 只要构造函数的原型对象出现在实例对象的原型链中都会返回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用于判断 一个对象是否是另一个对象的原型 (前面的是不是后面的)
注意点: 只要调用者在传入对象的原型链上都会返回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中对象和数组一样是可以遍历的
- 对象的遍历就是依次取出对象中所有的属性和方法
在JS中可以通过高级for循环来遍历对
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深拷贝
- 修改新变量的值不会影响原有变量的值
- 默认情况下基本数据类型都是深拷贝
10.2浅拷贝
修改新变量的值会影响原有的变量的值
默认情况下引用类型都是浅拷贝
class Person{ name = "lnj"; age = 34; } let p1 = new Person(); let p2 = p1; p2.name = "zs"; // 修改变量的值 console.log(p1.name); //变为zs
原理如下
10.3 对象深拷贝
以下两种只能拷贝基本数据类型
- 1.可以通过在新建一个对象,然后通过for循环遍历来实现拷贝(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;
执行完上面代码后会发生什么
这不就出事了吗
所以我们需要自定义一个函数来实现深拷贝,图示如下
function depCopy(target, source) { // 1.通过遍历拿到source中所有的属性 for(let key in source){ // console.log(key); // 2.取出当前遍历到的属性对应的取值 let sourceValue = source[key]; // console.log(sourceValue); // 3.判断当前的取值是否是引用数据类型 if(sourceValue instanceof Object){ // console.log(sourceValue.constructor); // console.log(new sourceValue.constructor); //这样如果是object就会创建一个object类型,如果是array类型就会创建一个array类型 let subTarget = new sourceValue.constructor; target[key] = subTarget; //递归调用实现拷贝, depCopy(subTarget, sourceValue); }else{ target[key] = sourceValue; } } }
11.数组高级API
11.1 遍历对象 forin
forin用来遍历对象,但是对象的属性是无序的, 所以forin循环就是专门用于遍历无序的东西的, 所以不推荐使用forin循环来遍历数组
for(let key in obj){ console.log(obj[key]); }
11.2 遍历数组
-
利用Array对象的forEach方法来遍历数组
forEach方法会自动调用传入的函数
每次调用都会将当前遍历到的元素和当前遍历到的索引和当前被遍历的数组传递给这个函数
arr.forEach(function (currentValue, currentIndex, currentArray) { // console.log(currentValue, currentIndex, currentArray); console.log(currentValue); }); //自己来实现一个 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循环来遍历数组
- for(let value of arr)
11.3数组的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; } }); //自己实现了一个 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 数组的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 数组的filter方法
将满足条件的元素添加到一个新的数组中
let newArray = arr.filter(function (currentValue, currentIndex, currentArray) { // console.log(currentValue, currentIndex, currentArray); if(currentValue % 2 === 0){ //要满足的条件 return true; } }); console.log(newArray); // [2, 4] //自己实现一下 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 数组的map方法(和filter的区别看一下)
将满足条件的元素映射到一个新的数组中(新数组的长度与原数组一样,没有就返回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] //自己实现 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 删除数组元素注意点
let len = arr.length; for(let i = 0; i < arr.length; i++){ arr.splice(i,1); } //删不干净,因为每删一次,数组中的元素会往前移,导致最后没法删了 //解决办法,从后往前删 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来删除数组中的元素, 数组的length属性不会发生变化 delete arr[i]; }
11.8 数组排序
// 如果元素是字符串类型, 那么比较的是字符串的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; } }); //如果数组中的元素是数值类型 //如果需要升序排序, 那么就返回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; }); //对象数组的排序 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中字符串可以看做一个特殊的数组, 所以大部分数组的属性/方法字符串都可以使用
// 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("-");//将数组换成一个字符串,用-链接 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.判断是否以指定字符串结尾 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>"; //现在直接写在``中就行 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);