# 延迟加载 JS 有哪些方式?
| <script type="text/javascript" src="./index.js"></script> |
| <script type="text/javascript" src="./a.js"></script> |
| <script type="text/javascript" src="./b.js"></script> |
| |
| <div id="app">app</div> |
| |
| console.log(document.querySelector('#app')) |
| |
| |
| console.log('aaa') |
| |
| |
| console.log('bbb') |
- 因为浏览器是从上个到下执行,
script
是在 app
上面的,那么 app 元素还没解析,上面 JS 代码就已经执行了,是获取不到 app 的
# <script async>
async
是和 HTML 解析同步的 (一起的),不是顺次执行 JS 脚本 (谁先加载完谁先执行)
| <script async type="text/javascript" src="./index.js"></script> |
| <script async type="text/javascript" src="./a.js"></script> |
| <script async type="text/javascript" src="./b.js"></script> |
| |
| <div id="app">app</div> |
| |
| console.log(document.querySelector('#app')) |
| |
| |
| console.log('aaa') |
| |
| |
| console.log('bbb') |
# <script defer>
| <script defer type="text/javascript" src="./index.js"></script> |
| <script defer type="text/javascript" src="./a.js"></script> |
| <script defer type="text/javascript" src="./b.js"></script> |
| |
| <div id="app">app</div> |
| |
| console.log(document.querySelector('#app')) |
| |
| |
| console.log('aaa') |
| |
| |
| console.log('bbb') |
| |
| |
| |
| |
| |
# JS 数据类型
# null & undefined 区别
- 作者在设计 JS 的都是先设计的 null (为什么设计 null:最初设计 JS 的时候借鉴了 java 的语言)
- null 会被隐式转换成 0,很不容易发现错误
- 先有 null 后有 undefined,出来 undefined 是为了弥补之前的坑
- 具体区别,JavaScript 的最初版本是这样区分的:
- null 是一个表示 ' 无 ' 的对象 (空对象指针),转为数值时为
0
;
- undefined 是一个表示 ' 无' 的原始值,转为数值时为
NaN
# == & === 有什么不同?
| 1 == '1' |
| true == 1 |
| [1,2,3] == '1,2,3' |
| null == undefined |
- string == number || boolean || number ... 都会隐式转换
- 通过 valueOf 转换 (valueOf () 方法通常由 JavaScript 在后台自动调用,并不显示)
| let arr = [1, 2, 3] |
| let str = '1,2,3' |
| |
| Object.prototype.valueOf = function () { |
| console.log('Object valueOf') |
| } |
| |
| Array.prototype.valueOf = function () { |
| console.log('Array valueOf') |
| } |
| |
| console.log(str == arr) |
- 上面例子会优先找自己原型的
valueOf
方法,所以会输出 Array valueOf
,但如果没有重写 Array.prototype.valueOf
,那么此时打印的就是 Object valueOf
!
- ===:除了比较值,还比较类型
| 1 === '1' |
| true === 1 |
| [1,2,3] === '1,2,3' |
| null === undefined |
# JS 微任务 & 宏任务
- JS 是单线程语言
- JS 代码执行流程:同步执行完 ==> 事件循环
- 同步的任务执行完了,才会执行事件循环的内容
- 进入事件循环:Ajax、定时器、事件 ......
- 事件循环中包含:微任务、宏任务
- 微任务:promise.then 、queueMicrotask、async/await ......
- 宏任务:setTimeout、setInterval ......
- 要执行宏任务的前提是清空所有微任务
- 事件循环的整体流程
- 先清空 call stack 中的同步代码
- 执行微任务队列中的微任务
- 尝试 DOM 渲染
- 触发 Event Loop 反复询问 callbackQueue 中是否有要执行的语句,有则放入 call back 继续执行
# JS 作用域考题
- 除了函数外,JS 是没有块级作用域
- 作用域链:内部可以访问外部的变量,但是外部不能访问内部的变量。如果内部有,优先查找到内部,如果内部没有就查找外部的
- 注意声明变量是用关键字 (let/const/var) 定义,没有写则是 (window.)
- 注意:JS 有变量提升的机制 (变量悬挂声明)
- 优先级:
声明变量 > 声明普通函数 >
变量提升
- 普通声明函数是不看写函数的时候顺序
- 下面栗几道简单的题
# 例题一
| function a() { |
| var b = 1 |
| function c() { |
| console.log(b) |
| var b = 2 |
| console.log(b) |
| } |
| c() |
| console.log(b); |
| } |
| a() |
# 例题二
- 这道题考察三点,作用域、按位取反运算符与立即执行函数
| var age = 16 |
| const res = ~ function () { |
| if (typeof age == 'undefined') { |
| var age = 17 |
| console.log('1' + age) |
| } else { |
| console.log('2' + age); |
| } |
| return age |
| }() |
| |
| console.log(res) |
# 例题三
| function foo() { |
| var bar = 10 |
| function bar() {} |
| console.log(bar) |
| } |
| foo() |
# 例题四
| function foo() { |
| console.log(bar) |
| var bar = 10 |
| function bar() {} |
| } |
| foo() |
# 例题五
| function foo(bar) { |
| var bar = 10 |
| function bar() {} |
| console.log(bar) |
| } |
| foo(233) |
# 例题六
| function foo() { |
| bar = 10 |
| console.log(bar) |
| var bar = 20 |
| console.log(bar) |
| } |
| foo() |
# JS 对象考题
| function Foo() { |
| this.a = '2.在Foo函数中添加的' |
| } |
| |
| Foo.prototype.a = '4.这是Foo原型添加到' |
| |
| let foo = new Foo() |
| |
| foo.a = '1.对象本身' |
| |
| foo.__proto__.a = '3.这是对象原型添加到' |
| |
| Object.prototype.a = '5.这是Object添加的' |
| |
| |
| console.log(foo.a) |
# 考题一
# 考题二
- 该题考察对象的 key 都是字符串类型,所以会被覆盖掉
| const lain = { |
| name: 'lain' |
| } |
| const saber = lain |
| saber.name = 'saber' |
| |
| console.log(lain) |
| |
| ~function () { |
| console.log(a) |
| var a = 1 |
| }() |
- 输出结果的顺序
{name: 'saber'} undefined
# JS 作用域 + this 指向 + 原型考题
# 考题一
| function Foo() { |
| getName = function () { |
| console.log(1) |
| } |
| return this |
| } |
| |
| Foo.getName = function () { |
| console.log(2) |
| } |
| |
| Foo.prototype.getName = function () { |
| console.log(3) |
| } |
| |
| var getName = function () { |
| console.log(4) |
| } |
| |
| function getName() { |
| console.log(5) |
| } |
| |
| |
| Foo.getName() |
| |
| getName() |
| |
| Foo().getName() |
| |
| getName() |
| |
| new Foo().getName() |
# 考题二
| var o = { |
| a: 10, |
| b: { |
| fn: function () { |
| console.log(this.a) |
| console.log(this) |
| } |
| } |
| } |
| |
| o.b.fn() |
- 输出结果的顺序是
undefined { fn: [Function: fn] }
# 考题三
| window.name = 'Hello World' |
| |
| function Foo() { |
| this.name = 232 |
| } |
| |
| Foo.prototype.getFoo = function () { |
| console.log(this) |
| return this.name + 1 |
| } |
| |
| let foo = new Foo() |
| let funFoo = foo.getFoo |
| |
| console.log(funFoo()) |
- 输出结果的顺序是
Window 'Hello World1'
# 考题四
| var length = 10 |
| |
| function fn() { |
| return this.length + 1 |
| } |
| |
| var obj = { |
| length: 5, |
| test1: function () { |
| return fn() |
| } |
| } |
| |
| obj.test2 = fn |
| |
| console.log(obj.test1()) |
| console.log(obj.test2()) |
| console.log(fn() === obj.test2()) |
| console.log(obj.test1() == obj.test2()); |
输出结果的顺序是 Window 'Hello World1'
# JS 判断变量是不是数组,你能写出哪些方法?
# Array.isArray
| const arr = [1, 2, 3] |
| const str = 'Hello World' |
# instanceof
| const arr = [1, 2, 3] |
| const str = 'Hello World' |
| |
| Array.isArray(arr) |
| Array.isArray(str) |
# Object.prototype.toString.call
| const arr = [1, 2, 3] |
| const str = 'Hello World' |
| |
| Object.prototype.toString.call(arr) === '[object Array]' |
| Object.prototype.toString.call(str) === '[object Array]' |
| |
| |
| Object.prototype.toString.call(arr).indexOf('Array') > -1 |
| Object.prototype.toString.call(str).indexOf('Array') > -1 |
# Array.prototype.isPrototypeOf
| const arr = [1, 2, 3] |
| const str = 'Hello World' |
| |
| Array.prototype.isPrototypeOf(arr) |
| Array.prototype.isPrototypeOf(str) |
# constructor
| const arr = [1, 2, 3] |
| const str = 'Hello World' |
| |
| arr.constructor.toString() === 'function Array() { [native code] }' |
| str.constructor.toString() === 'function Array() { [native code] }' |
| |
| |
| const arr = [1, 2, 3] |
| const str = 'Hello World' |
| arr.constructor.toString().indexOf('Array') > -1 |
| str.constructor.toString().indexOf('Array') > -1 |
# slice、splice 是否改变原数组
| const arr1 = [1, 2, 3, 4, 5] |
| const arr2 = arr1.slice(0, 2) |
| |
| console.log(arr1) |
| console.log(arr2) |
splice
可以插入、删除、替换
- 返回删除的元素,会改变数组
| const arr1 = [1, 2, 3, 4, 5] |
| const arr2 = arr1.splice(0, 2, '插入') |
| |
| console.log(arr1) |
| console.log(arr2) |
# JS 数组去重
# Set
| const arr = [1, 2, 1, 3, 2, 4, 4, 5, 6, 6] |
| |
| Array.from(new Set(arr)) |
| [...new Set(arr)] |
# unique 函数
| const arr = [1, 2, 1, 3, 2, 4, 4, 5, 6, 6] |
| |
| function unique(arr) { |
| const newArr = [] |
| for (let i = 0; i < arr.length; i++){ |
| if (newArr.indexOf(arr[i]) === -1) { |
| newArr.push(arr[i]) |
| } |
| } |
| return newArr |
| } |
| |
| unique(arr) |
# 排序方式
| const arr = [1, 2, 1, 3, 2, 4, 4, 5, 6, 6] |
| |
| function unique(arr) { |
| const newArr = [] |
| arr = arr.sort() |
| for (let i = 0; i < arr.length; i++){ |
| if (arr[i] !== arr[i - 1]) { |
| newArr.push(arr[i]) |
| } |
| } |
| return newArr |
| } |
| |
| unique(arr) |
# 找出多维数组最大值
| const arr =[[1,2,3,4], [5,6,7,8], [9,10,11,12]] |
| |
| function maxArr(arr) { |
| const newArr = [] |
| arr.forEach(item => { |
| newArr.push(Math.max(...item)) |
| }) |
| return newArr |
| } |
| |
| maxArr(arr) |
# 找出字符串出现最多次数的字符以及次数
| const str = 'abbcccddddeeeee' |
| |
| function repeatMaxStr(str) { |
| |
| const hash = {} |
| for (let i = 0; i < str.length; i++){ |
| if (!hash[str[i]]) { |
| hash[str[i]] = 1 |
| } else { |
| hash[str[i]]++ |
| } |
| } |
| |
| |
| let max = 0 |
| for (const key in hash) { |
| if (hash[key] > max) max = hash[key] |
| } |
| |
| |
| let res = {} |
| for (const key in hash) { |
| if (hash[key] == max) { |
| res[key] = max |
| } |
| } |
| |
| return res |
| } |
| |
| repeatMaxStr(str) |
# 闭包
-
闭包是什么?
- 闭包 是一个函数加上到创建函数的作用域的连接,闭包关 '' 闭 '' 了函数的自由变量
-
闭包可以解决什么问题 [闭包的优点]
- 内部函数可以访问到外部函数的局部变量
- 闭包可以解决的问题
| <ul> |
| <li>1</li> |
| <li>2</li> |
| <li>3</li> |
| </ul> |
| |
| <script> |
| const lis = document.querySelectorAll('ul li') |
| for (var i = 0; i < lis.length; i++) { |
| (function (i) { |
| lis[i].onclick = function () { |
| console.log(i) |
| } |
| lis[i] = null |
| }(i)) |
| } |
| </script> |
-
闭包的缺点
- 变量会驻留在内存中,造成内存损耗问题
- 解决:把闭包的函数设置为 null
lis[i] = null
- 内存泄露:可以提到 IE
# 原型链
-
原型可以解决什么问题?
-
谁有原型
-
对象查找属性或者方法的顺序
- 对象本身 -> 构造函数 -> 对象原型 -> 构造函数原型 -> 当前原型的原型
-
原型链