目录
一、严格模式与非严格模式
在代码前加
"use strict"
或在class类里书写都会开启严格模式。
开启严格模式后,JS有以下变化:
变量:
1、变量使用前必须声明 (此时,不声明就使用会直接报错)
2、不能直接用delete删除变量 (非严格模式的静默失败->严格模式的直接报错)
对象:
(非严格模式静默失败->严格模式的直接报错)(enumerable 不会报错)
1、对象的属性也不能直接使用delete来进行删除了
函数:
1、参数必须唯一 (非严格模式支持参数重复)
2、实参和形参不再具有映射关系 (非严格模式的arguments实参被修改,形参也会对应更改;在严格模式下,这两种参数一方被修改,另一方不会被一起更改)
3、不能使用argument的callee方法和caller
4、eval、agument类似这样的关键词名不得作为标识符,包括变量名、函数名等
5、不允许使用eval()、with()这两个方法 (这两个方法可能带来风险)
6、不允许出现八机制数 (因为八机制数开头是0会有歧义)
7、普通函数在全局里的this变成了undefined (非严格模式时,普通函数在全局里的this指向window)
二、双等于三等的区别
1、“==”表示:equality -> 等同的意思,“==”两个等号使用时,若两边值的类型不同时,系统会先进行类型转换,再进行比较;
2、“==”表示:identity -> 恒等的意思,“===”三个等号使用时,系统不会进行类型转换,若类型不同,就表示一定不等的;
3. 简单说明使用三个等号(===)的判断规则:
(1)如果类型不同,就一定不相等
(2)如果两个都是数值,并且是同一个值,那么相等;如果其中至少一个是NaN,那么不相等。(判断一个值是否是NaN,只能使用isNaN( ) 来判断)
(3)如果两个都是字符串,每个位置的字符都一样,那么相等,否则不相等。
(4)如果两个值都是true,或是false,那么相等
(5)如果两个值都引用同一个对象或是函数,那么相等,否则不相等
(6)如果两个值都是null,或是undefined,那么相等
4. 简单说明使用两个等号(==)的判断规则:
(1)如果两个值类型相同,再进行三个等号(===)的比较
(2)如果两个值类型不同,也有可能相等,需根据以下规则进行类型转换在比较:
1)如果一个是null,一个是undefined,那么相等
2)如果一个是字符串,一个是数值,把字符串转换成数值之后再进行比较
3)另外两个NaN是不相等的。
三、防抖和节流
防抖:单位时间内,频繁触发事件,只执行最后一次
常见场景:输入框输入
若频繁监听可能会出现抖动情况,解决方案有二,一是在输入结束后再点击触发事件,而是通过防抖操作解决。利用定时器可以实现防抖的功能,每次触发先清掉以前的定时器。
节流:单位时间内,频繁触发事件,只触发一次
常见场景:高频事件、快速点击、鼠标滑动监听、resize事件、scroll事件
类比场景:火箭发射
代码实现:利用定时器,定时器执行完毕,再开启定时器(不要打断)
第三方库也可实现,lodash库,利用里面的debounce(防抖)和throttle(节流)来做。
代码实现,见文章最后部分,闭包的7里的代码。
四、原型和原型链
原型:
每个普通函数都有prototype属性,称为原型。因为这个属性的值是一个对象,所以也叫原型对象。
作用:
1、存放一些属性和方法,共享给实例对象使用
2、在JavaScript中实现继承
如:
function Person(name, age) {
this.name = name
this.age = age
}
function Male() {
}
Male.prototype = new Person() //实现继承
const man=new Male()
console.log(man)
__proto__:
每个对象都有__proto__属性
作用:
这个属性指向它的原型对象
原型链:
对象都有__proto__属性,这个属性指向它的原型对象,原型对象也是对象,也有__proto__属性。原型对象的__proto__指向原型对象的原型对象,这样一层层形成的链式结构称为原型链。最顶层Object的prototype的__proto__指向null, 到null结束。
常见场景:调用某个对象的方法,若在对象的最近一级有该方法就直接调用,若没有,就继续往上一级查找,找到后调用。
案例:
function Person() { } const person=new Person() person.__proto__===Person.prototype //true person.__proto__.constructor===Person.prototype.constructor //true person.constructor===Person //true Person.prototype.constructor===person.constructor //true
更多关于prototype见这篇:前端 JS面向对象 原型 prototype-CSDN博客
五、页面重绘和回流
重绘:
当元素的视觉效果发生变化,但几何属性(如位置和大小)没有改变时,浏览器会重新绘制元素。这包括改变元素的背景色、边框色、字体颜色等。
回流:
当元素的布局属性发生变化时,浏览器需要重新计算元素的位置和大小,并更新页面布局。这包括添加、删除或重新排列DOM节点,修改元素的尺寸、位置或样式等。
回流是一个相对昂贵的操作,因为它涉及到大量的计算和重新布局,频繁触发回流会导致性能问题,尤其是在处理复杂或大型的DOM结构时。
何时引起重绘?
1、视觉属性变化时:如颜色、背景、边框色、阴影色、文字内容颜色等发生改变时;
2、元素尺寸和位置不变时:重绘发生在元素尺寸和位置不变的情况下,只改变元素的外观属性,如背景颜色、字体颜色等;
3、元素隐藏时:将元素的visibility属性设置为hidden时,虽然会触发重排,但不会影响布局,只会触发重绘。
何时会引起回流?
1、添加或删除可见的Dom元素;
2、元素位置发生变化;
3、元素尺寸发生变化 (包括外边距、内边距、边距大小、高度和宽度等);
4、内容发送变化,比如文本变化或图片被另一个不同尺寸的图片代替;
5、浏览器窗口尺寸发生变化(因为回流是根据视口大小来元素位置和大小的)
回流和重绘的联系?
回流一定引起重绘,而重绘不一定会回流。
六、script标签async和defer
script 标签 | JS 执行顺序 | 是否阻塞解析 HTML | DOMContentLoaded回调 |
---|---|---|---|
<script> | 依次 | 是 | 等待 |
<script async> | 先下载完先执行 | DOM未解析完时阻塞 | 不等待 |
<script defer> | 先下载完所有defer再依次执行 | 否 | 等待 |
1、默认情况下,遇到script标签会阻塞下面HTML的解析,先解析完成script标签里的内容,再继续执行解析。(同步解析,依次进行)
2、async情况下,遇到script标签系统不会阻塞HTML的解析,而是创建一个异步路径来进行async脚本的解析。一边解析HTML一遍解析async,当async解析完毕后,系统才会阻塞HTML的解析并立即执行async script里的内容。(解析时不阻塞HTML解析,解析完毕执行时才阻塞HTML,两个解析相互独立进行)
3、defer情况下,不会阻塞HTML解析,直到HTML解析完毕后,defer在最后执行。(DOM元素渲染完毕后执行)
使用情景:
1、默认情况适合一般脚本的顺序执行;
2、async情况适合处理一些无需阻塞并且与DOM元素无关的操作。
(注:这种情况下,执行DOM元素的操作是不稳定的。当DOM元素很少时,可能async有时会比HTML晚解析慢,从而后执行,有时执行正常有时执行失败。但当DOM元素过大需要更多时间解析时,很可能async都已经开始执行了,但DOM还未解析完毕,当async里的代码操作DOM元素时,就可能出现获取不到DOM元素的情况)
3、defer情况适合需要在页面加载完毕后的代码执行,如需要操作DOM的脚本执行。
七、普通函数和箭头函数的区别
1、this绑定:
箭头函数:没有自己的this上下文,而是继承父类的this; (不能使用apply、bind、call来修改this指向)(箭头函数的this在定义时就已确定,之后不得修改)
普通函数:this取决于函数的调用方式。(补充:在全局状态下,非严格模式this是window,严格模式下this是undefined)
2、arguments实参对象
箭头:不包含arguments;
普通:包含arguments对象 (补充:非严格模式下,实参arguments是和形参进行映射的,修改其中一个对应都会改变;严格模式下,并未有映射,修改其一不会修改另一个对应)
3、作用域和闭包
箭头:遵循作用域规则 (块级作用域)
普通:形成闭包
八、JS闭包
JavaScript的普通函数就是一个闭包。
1、闭包特点
1)函数嵌套函数
2)内层函数可以访问外层函数的变量和参数
2、闭包作用
1、防止变量和参数被垃圾回收机制回收(变量持久化)
2、防止变量和参数被外部污染 (变量只在内部可访问)
3、闭包风险
滥用可能会造成内存泄漏。
4、运用场景
1)常见闭包
function makeCounter() {
let counter = 0
return function () {
counter++
console.log(counter)
}
}
const counter = makeCounter()
counter() //1
counter() //2
counter() //3
2)实现模块化
const myModule=(function () {
let count=0
function increment(){
count++
}
function reset(){
count=0
}
function getCount(){
return count
}
return {
increment,
reset,
getCount
}
})()
myModule.increment()
console.log(myModule.getCount()) // 1
myModule.reset()
console.log(myModule.getCount()) // 0
3)缓存函数
function memorize(fn) {
const cache = {}
return function (arg) {
if (arg in cache) {
console.log("已调用缓存数据")
return cache[arg]
} else {
const result = fn(arg)
cache[arg] = result
return result
}
}
}
function expensiveCalculation(num) {
console.log("正在计算" + num + "的阶乘...")
let result = 1
for (let i = 2; i <= num; i++) {
result *= i
}
return result
}
const memorizedCalculation = memorize(expensiveCalculation)
console.log(memorizedCalculation(5)) //正在计算5的阶乘... 120
console.log(memorizedCalculation(5)) //已调用缓存数据 120
4)封装私有变量
function createPerson(name, age) {
let privateName = name
let privateAge = age
function getName() {
return privateName
}
function setName(newName) {
privateName = newName
}
function getAge() {
return privateAge
}
function setAge(newAge) {
privateAge = newAge
}
return {
getName,
setName,
getAge,
setAge
}
}
const person = createPerson("张三", 18)
console.log(person.getName()) //张三
console.log(person.getAge()) //18
person.setName("李四")
person.setAge(20)
console.log(person.getName()) //李四
console.log(person.getAge()) //20
5)实现函数柯里化
function add(a, b, c, d) {
return a + b + c + d
}
function curry(fn) {
return function curried(...args) {
if (args.length >= fn.length) {
return fn.apply(this, args)
} else {
return function (...moreArgs) {
return curried.apply(this, args.concat(moreArgs))
}
}
}
}
const curriedAdd = curry(add)
console.log(curriedAdd(1)(2)(3)(4)) //10
console.log(curriedAdd(1, 2)(3, 4)) //10
console.log(curriedAdd(1, 2, 3, 4)) //10
7)防抖和节流
function debounce(fn, wait) {
let timeoutId
return function (...args) {
clearTimeout(timeoutId)
timeoutId = setTimeout(() => {
fn.apply(this, args)
}, wait)
}
}
function throttle(fn, wait) {
let isThrottled = false
return function (...args) {
if (!isThrottled) {
isThrottled = true
setTimeout(() => {
fn.apply(this, args)
isThrottled = false
}, wait)
}
}
}
function handleInput() {
console.log("input changed")
}
const debouncedHandleInput = debounce(handleInput, 500)
const throttleHandleInput = throttle(handleInput, 500)
document.querySelector("input").addEventListener("input", debouncedHandleInput)
document.querySelector("button").addEventListener("click", throttleHandleInput)
总结到此!
标签:function,知识点,闭包,return,log,混淆,console,解析,梳理 From: https://blog.csdn.net/qq_50909707/article/details/143823761