ES6类的本质

ES6之前 → 通过 构造函数 + 原型 实现面向对象编程

  1. 构造函数有原型对象 prototype
  2. prototype里有 constructor 指向构造函数本身
  3. 可以通过原型对象添加方法
  4. 创建实例对象有__proto__原型指向构造函数的原型对象

类的本质

  1. class本质还是一个 function
  2. 类的所有方法都定义在类的prototype属性上
  3. 类创建的实例,里面也有__ proto __指向类的prototype原型对象
  4. class 写法只是让对象原型的写法更加清晰、更像面向对象编程的语言,所以ES6的类其实就是语法糖

语法糖就是一种便捷写法,简单理解,有两种方法可以实现同样的功能,但是一种写法更加清晰、方便,那么这个方法就是语法糖。


ES6面向对象

1. 类的创建

创建类
class name{// class body
}
// 创建实例  类必须使用new 实例化对象
var xx = new name();

2. constructor构造函数

  • 类的构造函数(默认方法),用于传递参数,返回实例对象.
  • 通过new生成对象实例时自动调用该方法.
  • 如果没有显示定义,类内部会自动创建.
class Person{constructor(name,age){this.name = name;this.age = age;}say(){console.log(this.name + 'Hello!');}
}
// 创建实例
var ldh = new Person('刘德华',18);
console.log(ldh.name)     // 刘德华
  • 方法之间不能添加逗号,方法也不需要function关键字.

3. 类的继承

子类可以继承父类的一些属性和方法。

class Father{   // 父类
}
class Son extends Father{  // 子类继承父类
}

实例:

class Father{constructor(surname){this.name = surname;}say(){console.log('你的姓是' + this.name);}
}
class Son extends Father{   // 子类继承了父类}
var ss = new Son('廖')
ss.say();  // 你的姓是廖
  • 继承中的属性或者方法查找原则:就近原则

4. super 关键字

调用父类的构造函数,也可以调用父类的普通函数。

class Father{constructor(surname){this.surname = surname;}  saySurname(){console.log('我的姓是' + this.surname);}
}
class Son extends Father{constructor(surname,firstname){  // 子承父类super(surname);  // 调用父类constructor(surname)this.firstname = firstname;   // 定义子类独有属性}sayFirstname(){console.log('我的名字是:' + this.firstname);}}
var ss = new Son('吴','彦祖')
ss.saySurname();
ss.sayFirstname();

调用父类的普通函数

class Father{say(){return '我是父类 -->'}
}
class Son extends Father{say(){// super.say() super 调用父类方法return super.say() + '的子类';}
}
var dome = new Son();
console.log(dome.say());   // 我是父类!的子类

super 必须在子类this之前调用。

子类在构造函数中使用super, 必须放到 this 前面 (必须先调用父类的构造方法,在使用子类构造方法)

注意点

  • ES6中没有变量提升(变量的提前声明),所以必须先定义类,才能实例化对象。
  • 类里面的共有属性和方法一定要加 this 使用。

类里面的 this 指向问题:

  • constructor里面的 this 指向实例对象,方法里的 this 指向这个方法的调用者。

一些方法:

用法 说明
insertAdjacentHTML(‘追加的位置’,‘追加的字符’) 用字符串创建的元素追加到父元素里面
beforeend 插入元素内部的最后一个子节点之后
ondblclick 双击事件
input.select() 文本框内容处于选中状态

双击禁止选中文字:

window.getSelection ? window.getSelection().removeAllRanges() : document.selection.empty();

insertAdjacentHTML(position,text);

// 创建li元素和section元素
var li = '<li class="liactive"><span>测试1</span><span class="iconfont icon-guanbi"></span></li>';// 把这两个元素追加到对应的父元素里面  beforeend(父元素内容的后面)
that.ul.insertAdjacentHTML('beforeend', li)

ES6新增语法

let 声明变量关键字

  1. let 声明的变量时只在所处于的块级有效

    if (true) {let b = 20;console.log(b)if (true) {let c = 30;}console.log(c);  // c is not defined
    }
    console.log(b) // b is not defined
    
  2. 不存在变量提升

    console.log(a);  // a is not defined
    let a = 20;
    
  3. 存在暂时性死区问题

    var num = 10
    if (true) {console.log(num);   // Cannot access 'num' before initializationlet num = 20;
    }
    
  4. 防止循环变量变成全局变量

    ```js
    for (let i = 0; i < 5; i++) {console.log(i);
    }
    // 依次输出 0 1 2 3 4 5
    ```
    

const 声明常量

作用:声明常量,常量就是值(内存地址) 不能变化的量

  1. 使用const关键字声明的常量具有块级作用域
     if (true) {const a = 10;if (true) {const a = 20;console.log(a);  // 20}console.log(a); // 10}console.log(a);   //  a is not defined```2.  声明常量时必须赋值```js
const PI; // Missing initializer in const declaration
  1. 常量赋值后,值不能修改, 但是可以替换数组内的值。
const = PI;
PI = 100; // assignment to constant variable.   const ary = [100, 200];
ary[0] = 'a';
ary[1] = 'b';
console.log(ary); // ['a', 'b'];
ary = ['a','b']; // assignment to constant variable.

解构赋值

ES6 中允许从数值中提取值,按照对应位置,对变量赋值。对象也可以实现解构。

数值解构

// 数组解构允许我们按照一一对应的关系从数组中提取值 然后将值赋值给变量
let ary = [1,2,3];
let [a, b, c, d, e] = ary;
console.log(a,b,c)  // 1 2 3
console.log(e)  // undefined// 如果解构不成功,变量的值为undefined
let [foo] = [];
let [bar, foo] = [1];

对象解构

let person = { name: 'zhangsan', age: 20 }; // 从 person对象中解构(匹配) name 和 age 属性
let { name, age } = person;
console.log(name); // 'zhangsan'
console.log(age); // 20    let {name: myName, age: myAge} = person; // myName myAge 属性别名
console.log(myName); // 'zhangsan'
console.log(myAge); // 20

箭头函数

写法:

() => {}
const fn = () => {}
  1. 函数体中只有一句代码,且代码的执行结果就是返回值,可以省略大括号
function sum(num1, num2) {return num1 + num2;
}
const sum = (num1, num2) => num1 + num2;
  1. 如果形参只有一个,可以省略小括号
function fn(v){retuen v;
}
const fn = v => v;
  1. 箭头函数不绑定this,没有自己的this关键字,在箭头函数中 this关键字将指向箭头函数定义位置中的this。
const obj = { name: '张三'}
function fn () {console.log(this); // obj return () => {console.log(this) // obj }
}
const resFn = fn.call(obj);
resFn();

箭头函数面试题:

var age = 100;
var obj = {age: 20,say: () => {alert(this.age)  // 100}
}
obj.say();

剩余参数

在最后的形参名前加 ...

  1. 剩余参数语法允许我们将一个不定数量的参数表示( 存放至)为一个数组.
function sum (first, ...args) {console.log(first); // 10 console.log(args); // [20, 30]
}
sun(10, 20, 30);
  1. 剩余参数和解构配合使用
let students = ['wangwu', 'zhangsan', 'lisi'];
let [s1, ...s2] = students;
console.log(s1); // wangwu
console.log(s2); // ['zhangsan', 'lisi']

ES6 新增方法

数组方法

扩展运算符 (展开语法)

  1. 将数组或者对象转为用逗号分隔的参数序列
let aty = [1, 2, 3];// ...ary  // 1, 2, 3
console.log(...ary);  // 1 2 3
console.log(1, 2, 3);  // 1 2 3
  1. 使用扩展运算符可以应用于合并数组
//方法一
let ary1 = [1, 2, 3];
let ary2 = [3, 4, 5];
let ary3 = [...ary1, ...ary2]; // 方法二
ary1.push(...ary2);
  1. 将类(伪)数组或可遍历对象转换为正真的数组
let oDivs = document.getElemenetsByTagName('div');   // HTMLCollection(10)
oDivs = [...oDivs];   // Array(7)

构造函数方法

  1. 将类(伪)数组或可遍历对象转换为正真的数组
 let arrayLike = {'0': 'a','1': 'b','2': 'c',length: 3};
let arr2 = Array.from(arrayLike); // ['a', 'b', 'c']
  1. 可以传入第二个参数,作用类似于数组的map方法,用来对每个元素进行处理,将处理后的值放入返回的数组。
let arrayLike = {"0": 1,"1": 2,"length": 2
}
let newAry = Array.from(aryLike, item => item * 2)

实例方法

find()

返回第一个符合条件元素,如果没有找到返回undefined

let ary = [{id: 1,name: '张三'
},{id: 2,name: '李四'
}];
let target = ary.find((item, index) => item.id == 2);

findIndex()

返回第一个符合条件元素索引位置,如果没有找到返回undefined

let ary =[1, 5, 10, 15];
let index = ary.findIndex((value,index) => value > 9);
console.log(index); // 2

includes()

查找某个数组是否包含给定的值,返回布尔值。

[1, 2, 3].includes(2); // true
[1, 2, 3].includes(4); // falselet ary = ["a", "b", "c"];
console.log(ary.includes('a'))

模板字符串

ES6 新增的创建字符串的方式,使用反引号定义。

1、解析变量

let name = '张三';
let sayHello = `hello,my name is ${name}`; // hello,my name is zhangsan

2、模板字符串换行

let resilt = {name: 'zhangsan',age: 20,sex:'男'
}
let html = ` <div><span>${result.name}</span><span>${result.name}</span><span>${result.name}</span>
</div> `;

3、在模板字符串可以调用函数

colst aryHello = function () {return '你好 世界!';
};let greet = `${sayHello()} 哈哈哈哈`;
console.log(greet);  // 你好 世界!

字符串方法

startsWith()

  • 表示参数字符串是否在原字符串的头部,返回布尔值

endsWith()

  • 表示参数字符串是否在原字符串的尾巴,返回布尔值
let str = 'Hello world!';
str.startsWith('Hello') // true
str.endsWith('!')       // true

repeat()

  • 将原字符串重复n次,返回一个新字符串。
'x'.repeat(3) // "xxx"
'hello'.repeat(2) // "hellohello"

Set 数据结构

  • ES6 提供了新的数据结构 Set 它类似于数组 但是成员的值都是唯一的 没有重复的值。
  • Set本身是一个构造函数,用来生成 Set 数据结构。
  • Set函数可以接受一个数组作为参数,用来初始化。
// 创建一个set() 数据结构
const s = new Set;const set = new Set([1, 2 ,3 ,4 , 5]);

set() 实例方法

方法 说明
add(value) 添加某个值,返回Set结构本身
delete(value) 表示删除是否成功, 返回布尔值
has(value) 表示该值是否为Set的成员, 返回布尔值
clear() 清除所有成员

示例:

const s = new Set();
s.add(1).add(2).add(3);     // 向 set 结构中添加值
s.delete(2)                 // 删除 set 结构中的2值
s.has(1)                    // 表示 set 结构中是否有1这个值 返回布尔值
s.clear()                   // 清除 set 结构中的所有值

set() 遍历

  • Set 结构的实例与数组一样,也拥有forEach方法。
// 遍历set数据结构 从中取值
const s = new Set(['a', 'b', 'c']);
s.forEach(value => {console.log(value)
})

正则表达式

正则表通常被用来提取和替换那些符合某个模式(规则)的文本。

列如验证表单、过滤敏感词、从字符串中获取我们想要的特定部分。

创建正则表达式

// 1. 利用 RegExp对象来创建 正则表达式
var regexp = new RegExp(/123/);
console.log(regexp);// 2. 利用字面量创建 正则表达式
var rg = /123/;

test() 检测正则表达式

正则对象方法,用于检测字符串是否符合该规则,返回 true 或 false

var rg = /123/;
console.log(rg.test(123));

边界符

边界符 说明
^ 表示匹配行首的文本(以谁开始)
$ 表示匹配行尾的文本(以谁结束)
  • ^ 和 $ 在一起使用时,表示必须是精确匹配

字符类

  1. 表示有一系列字符可供选择,只要匹配其中一个就可以了
  2. 所有可供选择的字符都放在方括号[] 内。
var rg = /[abc]/; // 只要包含有a 或者 包含有b 或者包含有c 都返回为true
console.log(rg.test('andy'));// 2. 字符组合 匹配26个英文字母和数字 1-9
/^[a-z0-9]$/.test('a')      // true// 3.  [^] 方括号内部 取反符^
/^[^abc]$/.test('a')        // false

量词符

量词符用来设定某个模式出现的次数。

量词 说明
* 匹配零次或更多次
+ 重复一次或更多次
? 重复零次或一次
{n} 重复n次
{n,} 重复n次或更多次
{n,m} 重复n到m次

括号使用

括号 说明
{} 量词符 括号内表示重复次数
[]字符集合 匹配方括号中的任意字符
()分组 表示优先级

预定义类

预定义类指的是某些常见模式的简写方式

预定义 说明 等价于
\d 匹配0-9之间的任一数字 [0-9]
\D 匹配所有0-9以外的字符 [ ^0-9]
\w 匹配任意的字母、数字和下划线 [A-Za-z0-9_]
\W 除所有字母、数字和下划线以外的字符 [ ^A-Za-z0-9_]
\s 匹配空格 (包括换行符、制表符、空格符等) [\t\r\n\v\f]
\S 匹配非空格的字符 [ ^\t\r\n\v\f]

全局匹配和忽略大小写

匹配模式 说明
g 全局匹配 (默认只匹配一个)
i 忽略大小写
gi 全局匹配+忽略大小写

正则表达式中的替换

使用replace() 方法替换,返回值是替换完毕的新字符串。

stringObject.replace(regexp/substr,replacement)

  • 第一个参数: 被替换的字符串 或者 正则表达式
  • 第二个参数: 替换的字符串
<textarea name="" id="message"></textarea> <button>提交</button><div></div>// 替换 replacevar text = document.querySelector('textarea');var btn = document.querySelector('button');var div = document.querySelector('div');btn.onclick = function() {div.innerHTML = text.value.replace(/激情|gay/g, '**');
}

ES5 的构造函数和原型

在 ES6之前 ,对象不是基于类创建的,而是用构建函数的特殊函数来定义对象和它们的特征。

构造函数是一种特殊的函数,使用 new 来初始化对象,把对象中一些公共的属性和方法抽取出来,然后封装到这个函数里面。

构造函数存在浪费内存问题

解决办法:我们可以把那些不变的方法,直接定义在 prototype 对象上,这样所有对象的实例就可以共享这些方法。

构造函数原型 prototype

构造函数通过原型分配的函数是所有对象所共享的。

原型的作用就是 共享方法。

对象原型 __ proto __

对象都会有一个属性__ proto __指向构造函数的 prototype原型对象,之所以我们对象可以使用构造函数 prototype 原型对象的属性和方法,就是因为对象有 __ proto __ 原型的存在。

__proto__对象原型的意义就在于为对象成员查找机制提供一个方向,或者说一条路线

实例:

function Star(uname, age) {this.uname = uname;this.age = age;}
// 我们的公共属性定义到构造函数里面, 公共的方法我们放到原型对象身上
Star.prototype.sing = function() {console.log('我会唱歌');
}
var ldh = new Star('薛之谦', 18);
var zxy = new Star('灵境', 19);
console.log(ldh.sing === zxy.sing);  // true
ldh.sing();
zxy.sing();//  对象身上系统自己添加一个 __proto__ 指向我们构造函数的原型对象 prototype
console.log(ldh); console.log(ldh.__proto__ === Star.prototype);   // true
  • __ proto __ 对象原型和原型对象 prototype 是等价的

  • 方法的查找规则: 首先先看ldh 对象身上是否有 sing 方法,如果有就执行这个对象上的 sing

  • 如果么有sing 这个方法,因为有__proto__的存在,就去构造函数原型对象 prototype 身上去查找sing这个方法。

constructor 构造函数

对象都会有一个属性 __ proto __ 指向构造函数的 prototype 原型对象,之所以我们对象可以使用构造函数 prototype 原型对象的属性和方法,就是因为对象有 __ proto __ 原型的存在。

constructor 主要用于记录该对象引用那个构造函数,它可以让原型对象重新指向原来的构造函数。

注意:很多情况下,我们需要手动的利用constructor 这个属性指回 原来的构造函数。

function Star(uname, age) {this.uname = uname;this.age = age;
}
// 很多情况下,我们需要手动的利用constructor 这个属性指回 原来的构造函数
// Star.prototype.sing = function() {//     console.log('我会唱歌');
// };
// Star.prototype.movie = function() {//     console.log('我会演电影');
// }
Star.prototype = {// 如果我们修改了原来的原型对象,给原型对象赋值的是一个对象,则必须手动的利用constructor指回原来的构造函数constructor: Star,sing: function() {console.log('我会唱歌');},movie: function() {console.log('我会演电影');}
}
var ldh = new Star('薛之谦', 28);
var zxy = new Star('灵境', 26);
console.log(Star.prototype);  // Object
console.log(ldh.__proto__);   // Object
console.log(Star.prototype.constructor);
console.log(Star.prototype.constructor  === ldh.__proto__.constructor); // true

构造函数、实例、原型对象之间的关系

原型链

成员查找机制

  • 当访问一个对象的属性时首先查找这个对象自身有没有该属性。

  • 如果没有就找原型(__proto__指向的 prototype)。

  • 如果还没有就查找原型对象的原型(Object的原型对象)。

  • 一直找到 Object 为止(null)。

原型对象里面方法里面的this 指向的是 这个方法的调用者, 也就是这个实例对象.

扩展内置对象方法

通过原型对象,对原来的内置对象进行扩展自定义的方法,比如给对象增加自定义求偶数和的功能。

Array.prototype.sum = function(){var sum = 0;for (var i = 0; i < this.length; i++) {sum += this[i];}return sum;}
var arr = [1, 2, 3];
console.log(arr.sum());

使用对象的形式创建方法会被覆盖(如: Array.prototype = {} ),所以数组和字符串内置对象只能是 Array.prototype.xxx = function(){} 的方式。

call() 方法

call (设置调用时指向的对象, 传递其他参数…)

  • 调用函数,并修改函数运行时this指向。
function fn(x, y) {console.log('我想喝手磨咖啡');console.log(this);console.log(x + y);
}
var o = {name: 'andy'
};// fn.call();  1. 使用call() 可以调用函数
// 2. 可以改变这个函数的this指向 此时这个函数的this 就指向了o这个对象
fn.call(o, 1, 2);

ES5里的继承

ES6之前并没有给我们提供extends继承。我们可以通过构造函数+原型对象模拟实现继承,被称为组合继承。

借用父构造函数继承属性和方法


// 1. 父构造函数
function Father(uname, age) {// this 指向父构造函数的对象实例this.uname = uname;this.age = age;
}
// 定义共享方法
Father.prototype.money = function() {console.log(100000);
};// 2 .子构造函数
function Son(uname, age, score) {   // 借用父构造函数继承属性 使用call()设置调用时this为 子构造函数的thisFather.call(this, uname, age);// this 指向子构造函数的对象实例this.score = score;
}
// Son.prototype = Father.prototype;  这样直接赋值会有问题,如果修改了子原型对象,父原型对象也会跟着一起变化
Son.prototype = new Father();// 如果利用对象的形式修改了原型对象,别忘了利用constructor 指回原来的构造函数
Son.prototype.constructor = Son;// 这个是子构造函数专门的方法
Son.prototype.exam = function() {console.log('孩子要考试');}
var son = new Son('张全蛋', 28, 100);
console.log(son);
console.log(Father.prototype);
console.log(Son.prototype.constructor);

ES5方法回顾

数组方法

  • 迭代(遍历)方法:forEach()、map()、filter()、 some()、every()

forEach() 遍历数组

  • forEach适合于你并不打算改变数据的时候(打印或存入数据库)
array.forEach(function(currentValue, index, arr))
  • currentValue : 每一个数组元素

  • index :数组当前项的索引

  • arr : 数组对象本身

map() 数组遍历

  • 适用于你要改变数据值的时候。速度更快且返回一个新的数组。
  • 还可以使用 复合(composition) (map(), filter(), reduce() 等组合使用。
let arr = [1, 2, 3, 4, 5];
// 使用map将每一个元素乘以它们自身,然后筛选出那些大于10的元素,最终结果赋值给arr2。
let arr2 = arr.map(value => value * value).filter(value => value > 10);
// arr2 = [16, 25]

filter() 筛选数组

筛选时直接返回一个新数组。

array.filter(function(currentValue, index, arr))

实例:

var arr = [12,66,88,99,56,4,3]
var newarr = arr.filter(function(value,index){// 筛选数组中大于20的值return value > = 20; //return value % 2 === 0; // 返回偶数
});
console.log(newarr);

some () 是否满足条件

返回布尔值, 找到第一个满足条件的元素则终止循环,不再继续查找。

var arr1 = ['red', 'pink', 'blue'];
var flag1 = arr1.some(function(value) {return value == 'pink';
});
console.log(flag1);  // true

filter返回一个新数组,含多个元素,find返回第一个符合条件元素。

indexof() 方法可返回某个指定字符串值在字符串中首次出现的位置。

字符串方法

trim() 删除两端空白字符

  • 不影响原字符串,返回一个新字符串。
var str = '   an  dy   ';
var str1 = str.trim();

对象方法

Object.keys()

获取对象自身所有的属性

var obj = {id: 1,pname: '小米',price: 1999,num: 2000
};
// 获取对象自身所有的属性
var arr = Object.keys(obj);
// 使用 Object.keys() 获取所有属性后遍历对象arr.forEach(function(value) {console.log(value);
})

Object.defineProperty()

定义新属性或修改原有的属性

Object.defineproperty(obj, prop, descriptor)

  • obj: 必需,目标对象
  • prop:必需,需定义或修改的属性的名字
  • descriptor: 必需,目标属性所拥有的特性

第三个参数 descriptor 说明:

  1. 必须以对象形式{} 书写
  2. value: 设置属性值 默认为undefined
  3. writable: 设置属性值是否可以被修改。true | false 默认为false不可修改。
  4. enumerable: 目标属性是否可以被遍历。
  5. configurable: 目标属性是否可以被删除或是否可以再次修改特特性。默认为false不可删除和修改。
var obj = {id: 1,pname: '小米',price: 1999
};Object.defineProperty(obj, 'address', {value: '小米科技有限公司',// 如果值为false 不允许修改这个属性值 默认值也是falsewritable: true,// enumerable 如果值为false 则不允许遍历, 默认的值是 falseenumerable: true,// configurable 如果为false 则不允许删除这个属性 默认为falseconfigurable: true
});
console.log(obj.address);

函数进阶

定义方法

调用方式

  1. 普通函数
function fn() {console.log('Hello !');}
// fn();   fn.call()
  1. 对象的方法
var o = {sayHi: function() {console.log('人生的巅峰');}
}
o.sayHi();
  1. 构造函数
function Star() {// do somesing...
};
new Star();
  1. 绑定事件函数
 btn.onclick = function() {};   // 点击了按钮就可以调用这个函数
  1. 定时器函数
setInterval(function() {}, 1000);  // 这个函数是定时器自动1秒钟调用一次
  1. 立即执行函数
// 立即执行函数是自调用
(function() {console.log('hello 2021');
})();

this 指向问题

调用方式的不同决定了this 的指向不同。

调用方式 this指向
普通函数调用 window
构造函数调用 (创建的)实例对象 原型对象里面的方法指向实例对象
对象方法调用 改方法所属对象
事件绑定方法 绑定事件的那个对象
定时器函数 window
立即执行函数 window

详解:

<button>点击</button><script>// 函数的不同调用方式决定了this 的指向不同// 1. 普通函数 this 指向windowfunction fn() {console.log('普通函数的this' + this);}window.fn();// 2. 对象的方法 this指向的是对象 ovar o = {sayHi: function() {console.log('对象方法的this:' + this);}}o.sayHi();// 3. 构造函数 this 指向 ldh 这个实例对象 原型对象里面的this 指向的也是 ldh这个实例对象function Star() {};Star.prototype.sing = function() {}var ldh = new Star();// 4. 绑定事件函数 this 指向的是函数的调用者 btn这个按钮对象var btn = document.querySelector('button');btn.onclick = function() {console.log('绑定时间函数的this:' + this);};// 5. 定时器函数 this 指向的也是windowwindow.setTimeout(function() {console.log('定时器的this:' + this);}, 1000);// 6. 立即执行函数 this还是指向window(function() {console.log('立即执行函数的this' + this);})();</script>

改变函数内部 this 指向:

使用 bind()、call()、apply() 三种方法可以优雅的解决函数内部的 this 指向问题。

call() 和 apply() 调用函数同时改变this指向

var o = {name: 'andy'
}
function fn(a, b) {console.log(this);console.log(a + b);
};
// 1. call() 调用函数同时改变this指向
fn.call(o, 1, 2);
//  apply() 调用函数同时改变this指向,参数必须是数组(伪数组)
fn.apply(o, ['pink']);

bind() 不调用函数改变this指向

示例: 我们有一个按钮,当我们点击了之后,就禁用这个按钮,3秒钟之后开启这个按钮:

<button>点击</button>
<button>点击</button>
<button>点击</button>var btns = document.querySelectorAll('button');for (var i = 0; i < btns.length; i++) {btns[i].onclick = function() {this.disabled = true;setTimeout(function() {this.disabled = false;}.bind(this), 2000);}}

总结:

相同点:

  • 都可以改变函数内部 this 指向。

不同点:

  • call 和 apply 会调用函数,并且改变函数内部this指向;
  • bind 不会调用函数,可以改变函数内部this指向

应用场景:

  • call() 经常做继承;
  • apply() 经常跟数组有关系,如借助数学对象求 max | min;
  • bind() 在不想调用函数又想改变this指向时使用,如改变定时器内部的this指向。

高阶函数

回调函数

高阶函数是对其他函数进行操作的函数,它接收函数作为参数或将函数作为返回值输出。

function fn(a, b, callback) {console.log(a + b);callback && callback();
}
fn(7, 10, function() {console.log('我是最后调用的');
});

函数也是一种数据类型,同样可以作为参数,传递给另一个参数使用,最典型的就是作为回调函数

递归函数

如果一个函数在内部可以调用其本身,那么这个函数就是递归函数

  1. 递归函数的作用和循环效果一样
  2. 由于递归很容易发生 “栈溢出”错误,所以必须要加 退出条件 return

实例: 利用递归函数求1~n的阶乘

// 利用递归函数求1~n的阶乘 1 * 2 * 3 * 4 * ..n
function fn(n) {if (n == 1) {return 1;}return n * fn(n - 1);
}
console.log(fn(3));
console.log(fn(4));// 详细思路 假如用户输入的是 3
//return  3 * fn(2)
//return  3 * (2 * fn(1))
//return  3 * (2 * 1)
//return  3 * (2)
//return  6

实例2: 利用递归函数求斐波那契数列 (兔子序列)

//   1、1、2、3、5、8、13、21...
// 用户输入一个数字 n 就可以求出 这个数字对应的兔子序列值
// 我们只需要知道用户输入的n 的前面两项(n-1 n-2)就可以计算出n 对应的序列值function fb(n) {if (n === 1 || n === 2) {return 1;}return fb(n - 1) + fb(n - 2);
}
console.log(fb(3));
console.log(fb(6));

闭包

变量作用域

分为全局变量局部变量

  1. 函数内部可以使用全局变量
  2. 函数外部不可以使用局部变量
  3. 当函数执行完毕,本作用域的局部变量将会被销毁。

什么是闭包 ?

一个作用域可以访问另外一个函数内部的局部变量。

闭包的作用:

延伸了变量的作用范围

我们函数外面的作用域可以访问函数内部的局部变量。

 function fn1(){    // fn1 就是闭包函数var num = 10;function fn2(){console.log(num); // 10}fn2()}

思考题1:

var name = "The Window";
var obj1 = {name: "My Object",getNameFunc: function () {return function () {return this.name;};}
};console.log(obj1.getNameFunc()())    // The Window

思考题2 :

var name = "The Window";
var obj2 = {name: "My Object",getNameFunc: function () {var that = this;return function () {return that.name;};}
};
console.log(obj2.getNameFunc()())    // My Object

浅拷贝和深拷贝

  1. 浅拷贝只是拷贝一层,更深层次对象级别的值拷贝引用
  2. 深拷贝拷贝多层,每一级别的数据都会拷贝。

ES6新增方法可以浅拷贝

Object.assign(target, …sources)

var obj = {id: 1,name: 'andy',msg: {age: 18}
};
var o = {};
Object.assign(o, obj);

严格模式

  1. 为整个 js 脚本开启严格模式
<script>"use strict";console.log("这是严格模式。");
</script>

有的 script 基本是严格模式,有的 script 脚本是正常模式,这样不利于文件合并,所以可以将整个脚本文件放在立即执行的匿名函数之中,这样独立创建一个作用域而不影响其他 script脚本文件。

(function(){"use strict";var num = 10;function fn(){}
})();

为函数开启严格模式

// 此时只是给fn函数开启严格模式
function fn() {'use strict';// 下面的代码按照严格模式执行
}

严格模式变化

变量常规:

  1. 没有变量声明提前,变量名必须先声明再使用;
  2. 严禁删除已声明的变量;

严格模式下 this 指向问题:

  1. 严格模式下全局作用域中函数中的this 是 undefined,非Windows;
  2. 构造函数不加new调用,this 会报错;
  3. new 实例化的构造函数指向创建的对象实例;
  4. 定时器 this 还是指向 window;
  5. 事件、对象还是指向调用者;
  6. 严格模式下函数里面的参数不允许有重名。

JavaScript 进阶面向对象ES6相关推荐

  1. JavaScript进阶面向对象ES6整合篇

    整合篇整体内容比较多,详细点可以查询目录 文章目录 一.JavaScript面向对象 1.面向对象编程介绍 2.ES6中的类和对象 3.类的继承 4.案例:面向对象案例 二.构造函数和原型 1. 构造 ...

  2. JavaScript进阶(四)

    JavaScript进阶(四) 2019版黑马程序员javaScript进阶面向对象ES6 122集教程,哔哩哔哩链接:https://www.bilibili.com/video/BV1Kt411w ...

  3. JavaScript进阶(二)

    JavaScript进阶(二) 2019版黑马程序员javaScript进阶面向对象ES6 122集教程,哔哩哔哩链接:https://www.bilibili.com/video/BV1Kt411w ...

  4. JavaScript进阶(三)

    JavaScript进阶(三) 2019版黑马程序员javaScript进阶面向对象ES6 122集教程,哔哩哔哩链接:https://www.bilibili.com/video/BV1Kt411w ...

  5. JavaScript进阶(一)

    JavaScript进阶(一) 2019版黑马程序员javaScript进阶面向对象ES6 122集教程,哔哩哔哩链接:https://www.bilibili.com/video/BV1Kt411w ...

  6. 前端学习笔记——JavaScript进阶

    提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档 文章目录 前言 一.JavaScript 面向对象 1. 面向对象编程介绍 1.1 两大编程思想 1.2 面向过程编程 1.3 面向对 ...

  7. JavaScript进阶-高级特性及ES6

    任务 对象的扩展 let和const Object.keys() for...of 扩展运算符 Set 和 Map 模版字符串 默认参数 rest参数 箭头函数 解构赋值 对象的扩展 方法的简写 // ...

  8. JavaScript 进阶教程(1)--面向对象编程

    目录 1 学习目标 2 面向对象介绍 2.1 什么是对象 2.2 什么是面向对象 2.3 JavaScript 中面向对象的基本体现 3 JavaScript 如何创建对象 3.1 字面量方式 3.2 ...

  9. JavaScript进阶1-学习笔记

    文章目录 JavaScript进阶1 预解析 作用域 面向对象的写法 JavaScript进阶1 预解析 //预解析 //1) console.log(a); var a = 1; //解析过程 va ...

最新文章

  1. ndk 不用java_使用NDK创建及配置C++程序(原生纯C++项目,不包含JAVA代码)
  2. error LNK2019: 无法解析的外部符号,该符号在函数 _main 中被引用的解决方法
  3. CentOS6.5菜鸟之旅:安装VirtualBox4.3
  4. Perl 中的正则表达式
  5. 让 Hangfire 使用 MongoDB 存储
  6. Python字典(Dictionary)的setdefault()方法的详解,字典中的赋值技巧
  7. 自动驾驶面试题汇总(2022秋招题库)——持续更新
  8. King Arthur
  9. java多线程在单例模式下是否需要同步
  10. 网页游戏开发入门教程三(简单程序应用)
  11. Hibernate 学习的书-夏昕(3)
  12. python word 表格复制_python实现同一word中的表格分别提取并保存到不同文件下
  13. 苹果税务信息填写教程
  14. 洛谷 P3939 数颜色(主席树)
  15. 人脸识别入门论文《Deep Facial Expression Recognition: A Survey》学习笔记
  16. STM32F4应用-GPIO
  17. 单臂路由器互联VLAN实验-Cisco Packet Tracer
  18. 描写冬天的唯美诗句,你想知道的都在这里!
  19. python文件中单词的删除_使用python删除文件中的多余单词
  20. 软件测试需要学什么(软件测试人员怎么入行)?

热门文章

  1. 数论相关_最大公约数最小公倍数
  2. USACO-Section 1.3 Combination Lock[...]
  3. 深度信念网络(DBN)
  4. 非常简单的语音朗读功能
  5. 用python 将PDF中的表格转化为Excel
  6. wps表格数据显示e+、绿色三角、单引号‘解决方式
  7. 解决Error: Could not detect Mac OS X Version from sw_vers output: '10.14.3'
  8. python 实现根据问题在文章中找到答案
  9. Excel如何进行求和
  10. Thinkpad E570没有外音