js高级
一、箭头函数
1.语法
// 没有形参、没有返回值,两行业务
const func1 = () => {
console.log(100);
console.log(100);
}
func1();
// 没有形参、没有返回值,一行业务(一行时可以省略大括号,有大括号时要有return,没有时可省略)
const func2 = () => console.log(101);
func2();
// 一个形参、没有返回值,一行业务
const func3 = num => num + 100;
console.log(func3(100));
2.注意事项
①当形参只有一个时,可省略括号。当没有形参时,括号不能省略;当代码只有一行时,可省略{}和return。
②当return是一个对象格式,不能省略{},要在外面包一层()。因为会把对象格式的{}认成块级作用域
let arr = [1, 2, 3];
//这样是错误写法,因为会把{}认成块级作用域而不是对象格式
arr = arr.map((value) => {a: value});
// 正常写法
arr = arr.map((value) => {
return {a: value};
});
// 推荐写法: 对象格式要在外面包个小括号,等价于上面的写法
arr = arr.map((value) => ({a: value}));
console.log(arr);
二、数组的常用方法
1.foreach()
作用:遍历数组
const arr = [1,2,3,4,5];
arr.forEach(value => {
if (value > 3) {
return;
}
console.log(value);
});
2.map()
作用:遍历数组,处理后返回新数组,相当于for循环里用方法处理数据
let arr = ['张三', '李四', '王五'];
const ul = document.querySelector('ul');
ul.innerHTML = arr.map(value => `<li>${value}</li>`).join('');
3.every()
作用:检测数组里每个数是不是都为true,是就返回true,相当于&&
let arr = [1,2,3,4];
arr.every(value => value > 1) // 返回false,因为数组里有一个不符合条件
4.some()
作用:检测数组里是不是有一个数为true,是就返回true,相当于||
let arr = [1,2,3,4];
arr.some(value => value > 3) // 返回true,因为数组里有一个符合条件
特殊情况,every()对空数组使用返回true
5.filter()
作用:筛选数组
let arr = [1, 2, 3, 4, 5, 6, 7, 8];
// 筛选偶数
let newArr = arr.filter(value => value % 2 == 0);
console.log(newArr); // 返回 [2,4,6,8]
6.总结
①map()和foreach()
map()的速度大于forEach();map会返回一个新的数组,不会改变原来的数组,forEach不会返回新数组,允许对原数组进行修改;都有参数,item, index arr 。
②every()和some()
every()全部为真时才为真,some()一个为真则为真;every()对空数组使用返回true,some()对空数组使用为false
三、构造函数
1.语法
function CreatObj(name, age) {
this.name = name,
this.age = age
}
// prototype是原型对象,一般只添加方法,既解决了每创建一个新的对象时方法也占用了一个新的内存
CreatObj.prototype.say = function() {
console.log('这里是ava向晚');
}
// 这里的obj1叫做实例
const obj1 = new CreatObj('向晚', 18);
obj1.say();
2.构造函数的注意事项
①构造函数首字母建议大写
②构造函数不需要使用return就可以返回结果
③构造函数必须和new关键字进行配合
④构造函数不能使用箭头函数的语法,因为二者的this指向不同。构造函数指向的是实例,而箭头函数没有this,或者说它的this沿着作用域链向上查找,一般指向的是全局对象windom
3.new执行时做的事情
①在内存中创建一个新的空对象
②让this指向这个新对象
③执行构造函数的代码,给这个新对象添加属性和方法
④返回这个新对象
四、面向对象
1.理念
面向对象具有封装性和继承性,是一种万物皆对象的理念。通过静态属性和行为来描述对象,将重复的代码进行封装,让子对象继承父对象的属性和方法。
2.封装性
// 负责创捷元素,但不负责插入
function ImgHtml(src) {
const img = document.createElement('img');
img.src = src;
this.dom = img;
}
// 调用函数选择插入哪个父元素
ImgHtml.prototype.append = function(parentNode) {
document.querySelector(parentNode).append(this.dom);
}
// 生成图片
const img1 = new ImgHtml("../day01/images/01.jpg");
// 插入到li里
img1.append('li');
通过对创建对象(静态结构)和插入(方法)的封装,以实现减少重复操作
3.继承性
// 将重复的属性和方法封装到一个根对象中,如果有子对象需要就从根对象中继承过来(借调)
function Main(tag) {
const dom = document.createElement(tag);
this.dom = dom;
}
Main.prototype.append = function(select) {
document.querySelector(select).append(this.dom);
}
// 此时子对象需要创建标签的静态结构和插入到父元素的方法,就从根对象中拿过来
function ItemDiv(tag, content) {
// 继承的核心就是call的借调
Main.call(this, tag); // 被借的对象.call(借用的对象,借调用到的参数)
this.dom.innerHTML = content;
}
// 方法的借调的固定写法
ItemDiv.prototype.append = Main.prototype.append;
const div = new ItemDiv('div', '嘉然');
div.append('h1');
总而言之,面向对象的编程模式在面对大型项目时,可以帮助程序员更好地理解和上手代码。其封装和继承的特性可以大幅度减少冗余代码,而且也便于代码的维护
五、es6新语法
1.函数参数默认值
// msg = false为设置函数默认值,当函数未传参时,msg默认值为false,str默认值为false
const func = (msg = 'hello', str = false) => console.log(msg, str);
func(); // 输出 'hello',false
func('你好'); // 输出 '你好',false
2.对象属性简写
const uname = '嘉然';
const age = 18;
// 当属性名和属性值一致时可以简写
const obj = {
uname, // 表示 uname : uname
age, // age : age
say() { // 对象里的函数简写
console.log('猫中毒');
}
}
obj.say(); // 输出猫中毒
console.log(obj); // 输出{uname : '嘉然',age : 18}
3.解构
// 数组的解构, 对应顺序
const arr = [1, 2, 3, 4, 5];
const [a, b, c, d] = arr;
console.log(a, b, c, d); // 1 2 3 4
// 对象解构,对应属性名,不需要对应顺序
const obj = {
age: 14,
height: 160
}
const {height,age} = obj;
console.log(height, age); // 输出160 14
// 数组加默认值解构 ,如果数组有对应的值就输出对应的值,没有就输出默认值
const arr1 = [1, 2]
const [a1, b1, c1 = 3] = arr1;
console.log(a1, b1, c1); // 输出 1 2 3
// 对象加默认值解构,和上述一致
const obj1 = {
height1: 170,
age1: 19,
}
const {height1,age1,sex = '女'} = obj1;
console.log(height1, age1, sex); // 输出 170 19 '女'
六、拓展运算符、剩余运算符...
1.剩余
// 当...放在数组/对象里面时,就表示数组/对象剩余的元素,此时变量就是数组/对象格式
const [a, b, ...c] = [1, 2, 3, 4, 5]; // ...c就表示将数组的剩余元素放入c变量里
console.log(c); // [3,4,5] 此时c是数组格式
const {a1, ...d1} = {
a1: 1,
b1: 1,
c1: 1
}
console.log(d1); // {b1:1,c1:1} 此时的d1是对象格式
2.拓展
// 当...放在数组/对象外面时,就是将数组/对象里面的元素一一展开
const obj = {
a: 1,
b: 2,
c: 3
}
const obj1 = { ...obj,d: 4}
// 此时改变obj1的值不会影响到obj
obj1.a = 9;
console.log(obj); // {a:1,b:2,c:3}
console.log(obj1); // {a:9,b:2,c:3,d:4}
const arr = [1, 2, 3, 4, 5];
const arr1 = [...arr, 6, 7];
console.log(arr1); // [1,2,3,4,5,6,7]
七、数组去重
const arr = [1, 2, 1, 5, 5, 6];
// Set括号里可以接收数组参数,内部就会进行去重处理,相当于进行了数组转对象
const set = new Set(arr);
// 输出的对象里面就不会有重复的数据
console.log(set);
// 利用拓展运算符对象展开添加到数组,相当于进行了对象转数组
const newArr = [...set];
console.log(newArr); // 输出[1,2,5,6]
八、call apply bind的区别
1.修改this的区别
const one = {
name: 'one',
sayName(a, b) {
console.log(this.name, a, b);
}
}
const two = {
name: 'two'
}
// call apply bind都可以修改this的指向
one.sayName.call(two);
one.sayName.apply(two);
// bind修改指向后会返回一个新的函数,并不会自动调用
const func1 = one.sayName.bind(two);
func1();
call和apply在修改this指向时用法相同,bind则会返回一个新函数,并且需要去调用
2.传递参数的区别
const one = {
name: 'one',
sayName(a, b) {
console.log(this.name, a, b);
}
}
const two = {
name: 'two'
}
one.sayName.call(two, 1, 2);
// apply传递参数是数组格式
one.sayName.apply(two, [1, 2]);
// bind传递参数是在新函数里传递
const func1 = one.sayName.bind(two);
func1(1, 2);
call传递参数时直接写在传递对象后,用逗号隔开。
apply传递参数是以数组格式写在传递对象后。
bind传递参数则是在返回的新函数中传递
九、es6新增class关键字构造函数
1.利用class构造函数
// class是es6面向对象的写法,并不是对象格式,不用加,
class Person {
// 属性
constructor() {
this.name = '张三',
this.age = 18
}
// 行为
sayHi() {
console.log('hi');
}
}
const p1 = new Person();
p1.sayHi();
简洁明了,不需要像es5一样将行为分开写在原型上
2.class的继承
class Father {
constructor(name, age) {
this.name = name
this.age = age
}
sayName() {
console.log(this.name);
}
}
class Son extends Father { // extends是class的继承格式,可以继承父对象的所有属性和方法
// 属性不写在constructor而写在class大括号外的话,就不能接收参数
constructor(name, age, sex) {
// 要继承属性时constructor里一定要写super();
super(name, age); // 相当于 Father.call(this,name,age);
this.sex = sex;
}
}
const f1 = new Father('张三', 18);
const s1 = new Son('李四', 18, '男');
console.log(f1); // 输出{name:'张三',age:18}
console.log(s1); // 输出{name:'李四',age:18,sex:'男'}
f1.sayName();
// class继承就不用再写prototype原型了,有了extends就直接调用方法即可
s1.sayName();
代码更加优雅,不用再去写es5方法继承的固定写法 : 子.prototype.方法 = 父.prototype.方法
十、原型链
// 万物皆对象,写在原型上的方法不在元素内显示,而在原型内显示,并且其他类型的数据也能使用添加在 对象 原型的方法
Object.prototype.show = function(arr) {
console.log('这是添加在对象原型的方法');
}
Array.prototype.show = function(arr) {
console.log('这是添加在数组原型的方法');
}
const arr = [];
const str = '';
arr.show(); // 输出在数组原型上的show(),因为就近原则,先找arr上的show
str.show(); // 输出在对象原型的show(),因为没有定义str原型的show,所以通过原型链查找最后找到object上的show()
console.log(arr);