文章目录
(续)20. 常见网页特效案例
20.1 节流阀
-
防止轮播图按钮连续点击造成播放过快
-
节流阀目的:当上一个函数动画内容执行完毕,再去执行下一个函数动画,让事件无法连续触发
-
核心实现思路:利用回调函数,添加一个变量来控制,锁住函数和解锁函数
var flag = true; if (flag) { flag = false; // 函数动画内容(执行一次的内容) } animate(..., ..., function() { flag = true; })
20.2 返回顶部
- 滚动窗口至文档中的特定位置
window.scroll(x, y)
- 注意:x,y 不跟单位
(续)21. 移动端轮播图
21.1 触屏事件(touch)
-
常见的触屏事件:
触屏touch事件 说明 touchstart
手指触摸到一个 DOM 元素时触发 touchmove
手指在一个 DOM 元素上滑动时触发 touchend
手指从一个 DOM 元素上移开时触发
21.2 触摸事件对象(TouchEvent)
触摸列表 | 说明 |
---|---|
touches | 正在触摸屏幕的所有手指的一个列表 |
❗targetTouches | 正在触摸当前 DOM 元素上的一个手指的一个列表 |
changedTouches | 手指状态发生了改变的列表,从无到有,从有到无变化 |
- 当我们手指离开屏幕的时候,就没有了 touches 和 targetTouches 列表,但是会有 changedTouches
- 因为平时我们都是给元素注册触摸事件,所以重点记住 targetTouches
21.3 移动端拖动元素
-
touchstart、touchmove、touchend 可以实现拖动元素
-
但是拖动元素需要当前手指的坐标值,我们可以使用 targetTouches[0] 里面的 pageX 和 pageY
注意:手指移动也会触发滚动屏幕所以这里要阻止默认的屏幕滚动
e.preventDefault();
补充:
1. 过渡完成事件 transitionend
2. classList 属性
-
classList 属性是 HTML5 新增的一个属性,返回元素的类名。但是 ie10 以上版本支持。
-
该属性中用于在元素中添加,移除及切换 CSS 类。有以下方法
-
添加类:
element.classList.add('类名');
- 在后面追加类名不会覆盖以前的类名,注意前面不需要加 .
-
移除类:
element.classList.remove('类名');
-
切换类:
element.classList.toggle('类名');
- 有这个类就删除,没这个类就添加
-
21.4 click 延时解决方案
-
移动端 click 事件会有 300ms 的延时,原因是移动端屏幕双击会缩放页面
-
解决方案
-
禁止缩放。浏览器禁用默认的双击缩放行为并且去掉 300ms 的点击延迟。
<meta name="viewport" content="user-scalable=no">
-
利用 touch 事件自己封装这个事件解决 300ms 延迟。
-
原理就是:
- 当我们的手机触摸屏幕,记录当前触摸时间
- 当我们手指离开屏幕,用离开的时间减去触摸的时间
- 如果时间小于 150ms,并且没有滑动过屏幕,那么我们就定义为点击
function tap(obj, callback) { var isMove = false; var startTime = 0; // 记录触摸时候的时间变量 obj.addEventListener('touchstart', function(e) { startTime = Date.now(); // 记录触摸事件 }); obj.addEventListener('touchmove', function(e) { isMove = true; // 看看是否有滑动,有滑动算拖拽,不算点击 }); obj.addEventListener('touchend', function(e) { if(!isMove && (Date.now() - startTime) < 150) { // 如果手指触摸和离开时间小于 150ms 算点击 callback && callback(); // 执行回调含函数 } isMove = false; // 取反 重置 satrtTime = 0; }); } // 调用 tap(div, function() { // 执行代码 })
-
-
使用插件。fastclick 插件解决 300ms 延迟
-
(续)22. Swiper 插件的使用(移动端)
- 中文官网网址: https://www.swiper.com.cn/
- 使用方法
- 引入插件相关文件
- 按照规定语法使用
- 其他移动端常见插件
- superslide
- iscroll
(续)23. 框架概述
- 框架(大而全,一套解决方案):就是一套架构,它会基于自身的特点向用户提供一套较为完整的解决方案。框架的控制权在框架本身,使用者要按照框架所规定的某种规范进行开发。
- 插件(小而专一,某个功能的解决方案):一般是为了解决某个问题而专门存在,其功能单一,并且比较小。
- 前端常用的框架有 Bootstrap、Vue、Angular、React 等。既能开发 PC 端,也能开发移动端。
- Bootstrap框架的基本使用
- 引入js文件时,先引入 jquery.js 再引入 bootstarp.js
(续)24. 本地存储
24.1 本地存储特性
- 数据存储再用户浏览器中
- 设置、读取方便、甚至页面刷新不丢失数据
- 容量较大,sessionStorage 约 5M,localStorage 约 20M
- 只能存储字符串,可以将对象 JSON.stringify() 编码后存储
24.2 window.sessionStorage
- 生命周期为关闭浏览器窗口
- 在同一个窗口(页面)下数据可以共享
- 以键值对的形式存储使用
-
存储数据:
sessionStorage.setItem(key, value)
-
获取数据
sessionStorage.getItem(key)
-
删除数据
sessionStorage.removeItem(key)
-
删除所有数据
sessionStorage.clear
24.3 localStorage
- 生命周期永久生效,除非手动删除否则关闭页面也存在
- 可以多窗口(页面)共享(同一浏览器可以共享)
- 以键值对的形式存储使用
-
存储数据:
localStorage.setItem(key, value)
-
获取数据
localStorage.getItem(key)
-
删除数据
localStorage.removeItem(key)
-
删除所有数据
localStorage.clear
补充:change 事件 ——> 针对 checkbox 的事件
第六章 面向对象编程(JS 高级)
1. 面向对象编程介绍
1.1 两大编程思想
-
面向过程(POP):就是分析处解决问题所需要的步骤,然后用函数把这些步骤一步步实现,使用的时候再一个一个的依次调用即可。
- 举例:把大象装进冰箱,有三步,按照分析好的每一步,按照步骤解决问题。
-
面向对象(OOP):就是把事物分解成为一个个对象,然后由对象之间分工与合作。(以对象功能来划分问题,而不是步骤)
- 举例:把大象装进冰箱,面向对象做法:
- 先找出对象,并写出这些对象的功能:
- 大象对象
- 进去
- 冰箱对象
- 打开
- 关闭
- 使用大象和冰箱的功能
- 大象对象
- 先找出对象,并写出这些对象的功能:
- 面向对象的特性:
- 封装性
- 继承性
- 多态性
- 举例:把大象装进冰箱,面向对象做法:
-
两者对比:
- 面向过程
- 优点:性能比面向对象高,适合跟硬件联系很紧密的东西,例如单片机就采用的面向过程编程
- 缺点:没有面向对象易维护、易复用、易扩展
- 面向对象
- 优点:易维护、易复用、易扩展,由于面向对象有封装、继承、多态性的特性,可以设计出低耦合的系统,使系统更加灵活、更加易于维护
- 缺点:性能比面向过程低
- 面向过程
2. ES6 中的类和对象
2.1 对象
- 在 JS 中,对象是一组无序的相关属性和方法的集合,所有的事物都是对象。
- 对象是由属性和方法组成的:
- 属性:事物的特征
- 方法:事物的行为
2.2 类 class
- 在 ES6 中新增加了类的概念,可以使用 class 关键字声明一个类,之后以这个类来实例化对象。
- 类抽象了对象的公共部分,它泛指某一大类
- 对象特指某一个,通过类实例化一个具体的对象
2.3 创建类
class name {
// class body
}
// 创建实例
var xx = new name();
注意:类必须使用 new 实例化对象
2.4 类 constructor 构造函数
-
constructor() 方法是类的构造函数(默认方法),用于传递参数,返回实例对象,通过 new 命令生成对象实例时,自动调用该方法。如果没有显示定义,类内部会自动给我们创建一个 constructor()
class Star { // 共有属性放到 constructor 里面 constructor(uname, age) { this.uname = uname; this.age = age; } } // 创建实例 var xx = new name(); var ldh = new Star('刘德华', 18);
- 通过 class 关键字创建类,类名我们习惯性定义首字母大写
- 类里面有个 constructor 函数,可以接收传递过来的参数,同时返回实例对象
- constructor 函数,只要 new 生成实例时,就会自动调用这个函数,如果我们不写这个函数,类也会自动生成这个函数
- 生成实例 new 不能省略
2.5 类添加方法
class Star {
// 共有属性放到 constructor 里面
constructor(uname) {
this.uname = uname;
this.age = age;
}
sing(song) {
// console.log('我唱歌');
console.log(this.uname + song);
}
}
// 创建实例
var xx = new name();
var ldh = new Star('刘德华', 18);
ldh.sing('冰雨');
-
类里面所有的函数都不需要 function
-
多个函数方法之间不需要添加逗号分隔
3. 类的继承
3.1 语法格式(extends)
class Father {}
class Son extends Father {} // 子类继承父类
3.2 super 关键字
-
super 关键字用于访问和调用对象父类上的函数。可以调用父类的构造函数,也可以调用父类的普通函数
- 调用父类的构造函数
class Father { constructor(x, y) { this.uname = x; this.age = y; } sum() { console.log(this.x + this.y); } } class Son extends Father { constructor(x, y) { super(x, y); // 调用了父类中的构造函数 } } var son = new Son(1, 2); son.sum();
- 调用父类的普通函数
class Father { say() { return '我是爸爸'; } } class Son extends Father { say() { // console.log('我是儿子'); console.log(super.say() + '的儿子'); // super.say() 就是调用父类中的普通函数 say() } } var son = new Son(); son.say();
-
继承中的属性或者方法查找原则:(就近原则)
- 继承中,如果实例化子类输出一个方法,先看子类有没有这个方法,如果有就先执行子类的
- 继承中,如果子类里面没有这个方法,就去父类里面查找,如果有,就执行父类这个方法
-
注意:子类在构造函数中使用 super,必须放到 this 前面(必须先调用父类的构造方法,再使用子类构造方法)
3.3 三个注意点
- 在 ES6 中类没有变量提升,所以必须先定义类,才能通过类实例化对象
- 类里面的共有属性和方法一定要加 this 使用
- 类里面的 this 指向问题
- constructor 里面的 this 指向实例对象,方法里面的 this 指向这个方法的调用者。
3.4 面向对象思想的tab栏切换
-
补充(页面添加元素):
- 以前的添加元素做法:动态创建元素 createElement,但是元素里面内容较多,需要 innerHTML 赋值,再 appendChild 追加到父元素里面。
- 现在高级做法:利用
insertAdjacentHTML(position, text)
可以直接把字符串格式元素添加到父元素中。- position必须是[字符串]:‘beforebegin’(元素自身的前面)、‘afterbegin’(插入元素内部的第一个子节点之前)、‘beforeend’(插入元素内部的最后一个子节点之后)、‘afterend’(元素自身的后面)
-
补充(双击禁止选中文字):
- 双击事件是:
ondblclick
window.getSelection ? window.getSelection().removeAllRanges() : document.selection.empty();
- 双击事件是:
4. 构造函数和原型
4.1 构造函数
- JS 的构造函数可以添加一些成员,可以在构造函数本身上添加,也可以在构造函数内部的 this 上添加。通过两种方式添加的成员,就分别称为静态成员和实例成员
- 静态成员:在构造函数本上添加的成员称为静态成员,只能由构造函数本身来访问
- 实例成员**(通过 this 添加)**:在构造函数内部创建的对象称为实例成员,只能由实例化的对象来访问
4.2 构造函数的问题
- 构造函数方法很好用,但是存在浪费内存的问题
- 简单数据类型直接赋值,复杂数据类型会在内存中开辟新的空间。所以每通过构造函数创建一个实例,调用其中的方法就会在内存中开辟新的空间。
- 解决方法:构造函数原型
4.3 构造函数原型 prototype
-
原型是:一个对象,我们也称 prototype 为原型对象
-
原型的作用:共享方法。节约内存空间。
-
构造函数通过原型分配的函数是所有对象所共享的。
-
JS 规定,每一个构造函数都有一个 prototype 属性,指向另一个对象。注意这个 prototype 就是一个对象,这个对象的所有属性和方法,都会被构造函数所拥有
-
我们可以把那些不变的方法,直接定义在 prototype 对象上,这样所有对象的实例就可以共享这些方法。
function Star(uname, age) { this.uname = uname; this.age = age; /* this.sing = function() { console.log('我会唱歌'); } */ } Star.prototype.sing = function() { console.log('我会唱歌'); }; var ldh = new Star('刘德华', 18); var zxy = new Star('张学友', 19); ldh.sing(); // 我会唱歌
4.4 对象原型 __proto__
- 对象都会有一个属性
_proto_
指向构造函数的 prototype 原型对象 - 方法查找规则:首先先看 对象 身上是否有 该方法,如果有就执行这个对象上的方法,如果没有,因为有
__proto__
的存在,就去构造函数原型对象 prototype 身上去查找 该方法。 __proto__
对象原型和原型对象 prototype 是等价的__proto__
只是指明一个查找方向,实际开发中,不可以使用这个属性
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-CmMPPkw2-1656822494853)(C:\Users\WZX\AppData\Roaming\Typora\typora-user-images\image-20220630201802262.png)]
4.5 constructor 构造函数
-
对象原型(
__proto__
)和构造函数(prototype)原型对象里面都有一个属性 constructor 属性,我们称其为构造函数,因为它指回构造函数本身。 -
constructor 主要用于记录该对象引用了哪个构造函数
-
注意:
- 很多情况下,我们需要手动的利用 constructor 这个属性 指回原来的构造函数
- 如果我们修改了原来的原型对象,给原型对象赋值的是一个对象,则必须手动的利用constructor指回原来的构造函数
function Star(uname, age) { this.uname = uname; this.age = age; /* this.sing = function() { console.log('我会唱歌'); } */ } /* 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('刘德华', 18); var zxy = new Star('张学友', 19); ldh.sing(); // 我会唱歌 console.log(Star.prototype); console.log(ldh.__proto__); console.log(Star.prototype.constructor); console.log(ldh.__proto__.constructor);
4.6 构造函数、实例、原型对象三者之间的关系
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-RXo3NuxE-1656822494859)(C:\Users\WZX\AppData\Roaming\Typora\typora-user-images\image-20220630204851614.png)]
4.7 原型链
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-g8ZM1xrG-1656822494861)(C:\Users\WZX\AppData\Roaming\Typora\typora-user-images\image-20220630211651168.png)]
4.8 JS 的成员查找机制(规则)
- 当访问一个对象的属性(包括方法)时,首先查找这个对象自身有没有该属性
- 如果没有就查找它的原型(也就是
__proto__
指向的 prototype 原型对象) - 如果还没有就查找原型对象的原型(Object 的原型对象)
- 依次类推一直找到 Object 为止(null)
__proto__
对象原型的意义就在于为对象成员查找机制提供一个方向,或者说一条路线
4.9 this 指向
- 在构造函数中,里面 this 指向的是 实例对象
- 原型对象函数里面的 this 指向的是 实例对象
4.10 扩展内置对象(原型对象的应用)
-
可通过原型对象,对原来的内置对象进行扩展自定义方法。
-
例子:给数组增加自定义求和的功能
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()); // 6 console.log(Array.prototype);
-
-
注意:数组和字符串内置对象不能给原型对象覆盖操作
Array.prototype = {}
,只能是Array.prototype.xxx = function() {}
的方式
5. 继承
- ES6之前并没有给我们提供 extends 继承。我们可以通过构造函数+原型对象模拟实现继承,被称为组合继承
5.1 call()
-
调用这个函数,并且修改函数运行时的this指向
fun.call(thisArg, arg1, arg2, ...)
- thisArg:当前调用函数 this 的指向对象
- arg1,arg2:传递的其他参数
// call 方法 function fn(x, y) { console.log('我想和手磨咖啡'); console.log(this); console.log(x + y); // 3 } var o = { name: 'andy' }; // 1. call() 可以调用函数 fn.call(); // 2. call() 可以改变这个函数的 this 指向 fn.call(o, 1, 2); // 此时这个函数的 this 就指向了 o
5.2 借用构造函数继承父类型属性
// 借用父构造函数继承属性
// 1. 父构造函数
function Father(uname, age) {
// this 指向父构造函数的对象实例
this.uname = uname;
this.age = age;
}
// 2 .子构造函数
function Son(uname, age, score) {
// this 指向子构造函数的对象实例
Father.call(this, uname, age);
this.score = score;
}
var son = new Son('刘德华', 18, 100);
console.log(son);
5.3 借用原型对象继承父类型方法
// 借用父构造函数继承属性
// 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) {
// this 指向子构造函数的对象实例
Father.call(this, uname, age);
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('刘德华', 18, 100);
console.log(son);
console.log(Father.prototype);
console.log(Son.prototype.constructor);
6. ES5 中的新增方法
6.1 数组方法
-
迭代(遍历)方法:forEach()、map()【和forEach相似】、filter()、some()、every()【和some相似】
-
forEach()
-
arr.forEach(function(currentValue, index, arr))
-
currentValue:数组当前项的值
-
index:数组当前的索引
-
arr:数组对象本身
-
-
filter() 方法创建一个新的数组,主要用于筛选数组
-
arr.filter(function(currentValue, index, arr)) var arr = [12, 66,4, 88]; var newArr = arr.filter(function(value, index) { return value > 20; }) console.log(newArr); // [66, 88]
-
注意它直接返回一个新数组,返回所有满足条件的元素
-
-
some() 方法,查找数组中是否有满足条件的元素
-
arr.some(function(currentValue, index, arr))
-
注意它返回的是布尔值,找得到返回 true,找不到返回 false
-
如果找到第一个满足条件就终止循环不继续查找了
-
-
forEach() 和 some() 的区别
- forEach() 遇到 return 仍然会继续遍历数组
- filter() 遇到 return 仍然会继续遍历数组
- some() 遇到 return 终止循环,迭代效率更高
6.2 字符串方法
-
trim() 方法会从一个字符串的两端删除空白字符
-
str.trim()
-
trim() 方法不会影响字符串本身,返回的是一个新字符串
-
6.3 对象方法
-
Object.keys() 用于获取对象自身所有的属性
-
Object.keys(obj)
-
效果类似 for…in
-
返回一个由属性名组成的数组
-
-
Object.defineProperty() 定义对象中新属性或修改原有的属性
-
Object.defineProperty(obj, prop, descriptor) var obj = { id: 1, pname: '小米', price: 1999 } Object.defineProperty(obj, 'num', {value: 1000})
-
obj:必须,目标对象
-
prop:必须,需要定义或修改的属性的名字
-
descriptor:必须,目标属性所拥有的特性
- 以对象形式 {} 书写
- value:设置属性的值,默认 undefined
- writable:值是否可以重写。true|false 默认为 false
- enumerable:目标属性是否可以被枚举。true|false 默认为 false(不允许遍历这个属性)
- configurable:目标属性是否可以被删除或是否可以再次修改特性。true|false 默认为 false(不允许删除该属性同时也不允许再次修改第三个参数里面的特性【descriptor】)
-
7. 函数的进阶
7.1 函数的定义和调用
-
函数的定义方式
-
函数声明方式 function 关键字(命名函数)
-
函数表达式(匿名函数)
-
new Function('参数1', '参数2', '函数体');
(了解)-
var f = new Function('a', 'b ''console.log(a + b)'); f(1, 2); // 3
-
Function 里面参数都必须是字符串格式
-
这种方式执行效率低,也不方便书写,因此较少使用
-
所有函数都是 Function 的实例对象
-
函数也属于对象
-
-
-
函数的调用方式
// 函数的调用方式
// 1. 普通函数
function fn() {
console.log('人生的巅峰');
}
// fn(); fn.call()
// 2. 对象的方法
var o = {
sayHi: function() {
console.log('人生的巅峰');
}
}
o.sayHi();
// 3. 构造函数
function Star() {};
new Star();
// 4. 绑定事件函数
// btn.onclick = function() {}; // 点击了按钮就可以调用这个函数
// 5. 定时器函数
// setInterval(function() {}, 1000); 这个函数是定时器自动1秒钟调用一次
// 6. 立即执行函数
(function() {
console.log('人生的巅峰');
})();
// 立即执行函数是自动调用
7.2 this
-
函数内 this 的指向(一般指向我们的调用者)
// 函数的不同调用方式决定了this 的指向不同 // 1.** 普通函数 this 指向window ** function fn() { console.log('普通函数的this' + this); } window.fn(); // 2. 对象的方法 this指向的是对象 o var 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 指向的也是window window.setTimeout(function() { console.log('定时器的this:' + this); }, 1000); // 6. 立即执行函数 this还是指向window (function() { console.log('立即执行函数的this' + this); })();
-
改变函数内部 this 指向(bind()、call()、apply() 方法)
-
call 方法
fun.call(thisArg, arg1, arg2, ...)
- 既可以调用函数,又可以改变函数内 this 的指向
- call 的主要作用可以实现继承
-
apply 方法
-
fun.apply(thisArg, [argsArray])
-
既可以调用函数,又可以改变函数内 this 的指向
-
但是它的参数必须是数组(伪数组)
-
apply 的主要应用:比如我们可以利用 apply 借助于数学内置对象求最大值
var arr = [1, 66, 88, 99]; var max = Math.max.apply(Math, arr); console.log(max);
-
-
bind 方法
-
bind.call(thisArg, arg1, arg2, ...)
-
该方法不会调用函数,但是会改变函数内部 this 指向
-
返回由指定的 this 值和初始化参数改造的原函数拷贝
var o = { name: 'andy' }; function fn(a, b) { console.log(this); console.log(a + b); }; var f = fn.bind(o, 1, 2); f();
-
主要应用:如果有的函数不需要立即调用,但是又想改变这个函数内部的 this 指向此时使用 bind
-
例子:点击按钮后禁用,2s 后解锁按钮
// 1. 不会调用原来的函数 可以改变原来函数内部的this 指向 // 2. 返回的是原函数改变this之后产生的新函数 // 3. 如果有的函数我们不需要立即调用,但是又想改变这个函数内部的this指向此时用bind // 4. 我们有一个按钮,当我们点击了之后,就禁用这个按钮,3秒钟之后开启这个按钮 // var btn1 = document.querySelector('button'); // btn1.onclick = function() { // this.disabled = true; // 这个this 指向的是 btn 这个按钮 // // var that = this; // setTimeout(function() { // // that.disabled = false; // 定时器函数里面的this 指向的是window // this.disabled = false; // 此时定时器函数里面的this 指向的是btn // }.bind(this), 3000); // 这个this 指向的是btn 这个对象 // } 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); } }
-
-
bind()、call()、apply() 总结
- 相同点:都能改变函数内部 this 指向
- 区别:
- call、apply 会调用函数,并且改变函数内部 this 指向
- call、apply 传递的参数不一样, apply 必须以数组形式传递
- bind 不会调用函数,只改变 this 指向
- 只要应用场景
- call 经常做继承
- apply 经常跟数组有关系,比如借助于数学对象实现数组最大值最小值
- bind 不调用函数,但是还想改变 this 指向,比如改变定时器内部的 this 指向
-
8. 严格模式
8.1 什么是严格模式(ie10以上支持)
- 严格模式对正常的 JS 语义做出了一些更改:
- 消除了 JS 语法的一些不合理、不严谨之处,减少了一些怪异行为
- 消除代码运行的一些不安全之处,保证代码运行的安全
- 提高编译器效率,增加运行速度
- 禁用了在 ECMAScript 的未来版本中可能会定义的一些语法,为未来新版本的 JS 做法铺垫。
8.2 开启严格模式
- 严格模式可以应用到整个脚本或个别函数中。因此,严格模式分为为脚本开启严格模式和为函数开启严格模式
-
为脚本开启严格模式
-
<script> "use strict"; </script>
-
在所有语句之前放一个特定语句 “use strict”; (或 ‘use strict’; )
-
-
为函数开启严格模式
- 把 “use strict”; (或 ‘use strict’; )声明放在函数体所有语句之前。
8.3 严格模式中的变化
-
变量规定
- 在正常模式中,如果一个变量没有声明就赋值,默认是全局变量。严格模式禁止这种用法,变量都必须先用 var 命令声明,然后再使用。
- 严禁删除已经声明的变量。例如:delete x; 语法是错误的。
-
严格模式下 this 指向问题
- 以前全局作用域下 this 指向 window。
- 严格模式下全局作用域中函数中 this 指向 undefined
- 以前构造函数时不加 new 也可以调用,当普通函数调用,this 指向全局对象
- 严格模式下,如果构造函数不加 new 调用,this 指向 undefined,如果给它赋值会报错
- new 实例化的构造函数指向创建的对象实例
- 定时器里的 this 还是指向 window
- 事件、对象还是指向调用者
-
函数变化
- 函数不能有重名的参数
- 函数必须声明在顶层,新版本的 JS 会引入“块级作用域”(ES6 中已引入)。为了于新版本接轨,不允许在非函数的代码块内声明函数。
9. 高阶函数
-
高阶函数是对其他函数进行操作的函数,它接收函数作为参数或将函数作为返回值输出、
-
function fn1(callback) { callback && callback(); } fn1(funuction() {alert('hi')}) function fn2() { return function() {}; } f2();