变量类型和计算 值类型和引用类型,分别是什么,有什么区别 JS的基本数据类型 就是值类型 ,包括 Undefined, Null, Boolean, Number, String, Symbol JS的引用类型 ,统称为Object类型 ,包括 Object,Array,Date,RegExp,Function
1 2 3 4 5 6 7 8 9 const obj1 = { X : 100 }const obj2 = obj1let x1 = obj1.Xobj2.X = 101 x1 = 102 console .log(obj1) console .log(obj2) console .log(x1)
typeof 能判断哪些类型 typefof 能判断所有基本类型数据, 对于引用类型 :function => function null 、 数组、对象 => Object (null 属于例外情况)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 var a = null ;(!a && typeof a === 'object' ) var a;typeof a; a = null ; typeof a; a = true ; typeof a; a = 666 ; typeof a; a = "hello" ; typeof a; a = Symbol (); typeof a; a = function ( ) {} typeof a; a = []; typeof a; a = {}; typeof a; a = /aaa/g ; typeof a;
判断全局变量有无时需要用到:
1 2 3 4 5 6 7 if (DEUBG) { console .log('debugging is start' ) } if (typeof DEBUG !== 'undefined' ) { console .log('debugging is start' ) }
何时使用 === ,何时使用 == “ == 允许在相等比较中进行强制类型转换,而 === 不允许。”
JS深浅拷贝 在另一篇博文有详细介绍,链接:点此
类型转换: 在另一篇博文有详细介绍,链接:点此
原型和原型链 如何准备判断一个变量是数组? a instanceof Array
class 的原型本质? 原型和原型链的原理
jQuery 的基本结构?用 class 实现?要考虑插件和扩展性。 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 class jQuery { constructor (selector ) { const result = document .querySelectorAll(selector) const length = result.length for (let i = 0 ; i < length; i++) { this [i] = result[i] } this .length = length this .selector = selector get (index ) { return this [index] } each (fn ) { for (let i = 0 ; i < this .length ; i++) { const elem = this [i] fn(elem) } } on (type, fn ) { return this .each(elem => { elem.addEventListener(type, fn, false ) }) } } } const $p = new jQuery('p' )$p.get(1 ) $p.each((elem ) => console .log(elem.nodeName)) $p.on('click' , () => alert('clicked' )) jQuery.prototype.dialog = function (info ) { alert(info) } class myjQuery extends jQuery { constructor (selector ) { super (selector) } addClass (className ) { }, style ( ) { } }
在另一篇博文有详细介绍,链接:点此 作用域和闭包 this 的不同应用场景,如何取值? (1)作为普通函数 ,返回 window (2)使用 call 、apply 、bind ,返回绑定的参数 (3)作为对象方法被调用,返回对象本身 (4)在 class 方法中被调用,返回 实例 (5)箭头函数: 取的是上级作用域 的调用者
this 的值 是执行时 确定的,而不是函数定义时。
1 2 3 4 5 6 7 8 function fn1 ( ) { console .log(this ) } fn1() fn1.call({ x : 100 }) const fn2 = fn1.bind({ x : 200 }) fn2()
箭头函数中 和 setTimeout中 对 this的使用:
class 中 this的指向:实例本身
1 2 3 4 5 6 7 8 9 10 11 class People { constructor (name ) { this .name = name this .age = 20 } sayHi () { console .log(this ) } } const john = new People('john' )john.sayHi()
手写 bind 函数? 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 function fn1 (a , b, c ) { console .log('this: ' , this ) console .log(a, b, c) return 'this is fn1' } const fn2 = fn1.bind({x : 100 }, 10 , 20 , 30 ) const res = fn2()console .log(res)Function .prototype.bind1 = function ( ) { const args = Array .prototype.slice.call(arguments ) const t = args.shift() const self = this return function ( ) { self.apply(t, args) } }
在另一篇博文有详细介绍 call 、 apply、 bind,链接:点此
闭包是什么,实际开发中闭包的应用场景,举例说明: 闭包是作用域应用的特殊情况,有两种表现: (1)函数作为参数被传递
1 2 3 4 5 6 7 8 9 function print (fn ) { const a = 200 fn() } const a = 300 function fn ( ) { console .log(a) } print(fn)
(2)函数作为返回值被返回
1 2 3 4 5 6 7 8 9 function create ( ) { const a = 100 return function ( ) { console .log(a) } } const a = 200 const fn = create()fn()
闭包在开发中的实际应用: (1)隐藏数据,避免外界修改或污染某值 (2)做一个简单的 cache 工具
1 2 3 4 5 6 7 8 9 10 11 12 13 14 function createCache ( ) { const data = {} return { set : function (key, val ) { data[key] = val }, get : function (key ) { return data[key] } } } const c = createCache()c.set('x' , 100 ) c.get('x' )
10个 a 标签,点击弹出的序号是? 1 2 3 4 5 6 7 8 9 10 11 12 let i, afor (i = 0 ; i < 10 ; i++) { a = document .createElement('a' ) a.innerHTML = i + '<br>' a.addEventListener('click' , function (e ) { e.preventDefault() alert(i) }) document .body.appendChild(a) } for (let i = 0 ; ...)
作用域 和 自由变量 分别是什么? 作用域包括:全局作用域 、函数作用域 、块级作用域( ES6 新增 )。
1 2 3 4 5 6 7 8 9 10 11 if (true ) { let x = 100 } console .log(x) if (true ) { var y = 101 } console .log(y)
自由变量: (1)一个变量在当前作用域没有定义,但被使用了 (2)向上级作用域,一层一层的寻找,直至找到 (3)如果到全局作用域都没找到,则报错:is not defined (4)自由变量的查找,是在函数定义 的地方,向上级作用域查找。不是在执行的地方 。
let var const
异步 和 单线程 为什么说 JS 是单线程语言? JS 是单线程语言,只能同时做一件事。
为了利用多核 CPU 的计算能力, HTML5提出 Web Worker 标准,允许 JavaScript 脚本创建多个线程,但是子线程完全受主线程控制,且不得操作 DOM 。所以,这个新标准并没有改变 JavaScript 单线程的本质。
JS 和 DOM 渲染共用一个线程,因为 JS 可修改 DOM 结构
也是因为单线程机制,JS 发生请求 ,如果没有异步的话,会一直卡住接下来的 JS 运行,一直等待到结果返回,这样是不合理的,浪费了时间资源。
异步: 遇到等待(网络请求 、 定时任务)不能卡住
以 callback 回调函数形式
同步和异步的区别是什么? 同步会阻塞代码执行
异步不会阻塞代码执行, 就是为了处理 JS 单线程时的执行问题
Promise 的由来?callback hell 是什么? callback hell ,就是传说中的回调地狱。
1 2 3 4 5 6 7 8 9 $.get(url1, (data1 ) => { console .log(data1) $.get(url2, (data2 ) => { console .log(data2) $.get(url3, (data3 ) => { console .log(data3) }) }) })
Promise 有哪三种状态?如何变化? 待定( pending ): 初始状态,既没有被兑现,也没有被拒绝。 已兑现( fulfilled ): 意味着操作成功完成。 已拒绝( rejected ): 意味着操作失败。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 const p1 = new Promise ((resolve, reject ) => {}) console .log(p1) const p2 = new Promise ((resolve, reject ) => { setTimeout (() => { resolve() }) }) console .log(p2) const p3 = new Promise ((resolve, reject ) => { setTimeout (() => { reject() }) }) console .log(p3) const p4 = Promise .resolve(100 ) p4.then(data => { console .log(data) })
promise then 和 catch 的连接 then 正常返回 resolved ,里面有 throw Error 则返回 rejected catch 正常返回 resolved , 里面有 throw Error 则返回 rejected
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 const p3 = Promise .reject('my error' ).catch(err => { console .error(err) }) console .log('p3: ' , p3)p3.then(() => { console .log(1 ) }) const p4 = Promise .reject('my new Error' ).catch(err => { throw new Error ('catch err' ) }) console .log('p4: ' , p4) p4.then(() => { console .log(2 ) }).catch(err => { console .log('error' ) })
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 Promise .resolve().then(() => { console .log(1 ) }).catch(() => { console .log(2 ) }).then(() => { console .log(3 ) }) Promise .resolve().then(() => { console .log(1 ) throw new Error ('error1' ) }).catch(() => { console .log(2 ) }).then(() => { console .log(3 ) }) Promise .resolve().then(() => { console .log(1 ) throw new Error ('error1' ) }).catch(() => { console .log(2 ) }).catch(() => { console .log(3 ) }) (async function ( ) { console .log('start' ) const a = await 100 console .log('a: ' , a) const b = await Promise .resolve(200 ) console .log('b: ' , b) const c = await Promise .reject(300 ) console .log('c: ' , c) console .log('end' ) })()
手写用 Promise 加载一张图片? 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 const url = 'https://cdn.jsdelivr.net/gh/jonnzer/saveImgForYun@master/test1/this_1.webp' function loadImg (src ) { return new Promise ((resolve, reject ) => { const img = document .createElement('img' ) img.onload = () => { resolve(img) } img.onerror = () => { reject(new Error (`图片加载失败${src} ` )) } img.src = src document .body.appendChild(img) }) } loadImg(url).then((res ) => { console .log(res) return 2 }).then((res2 ) => { console .log(res2) }) async await 写法(async function ( ) { const img1 = await loadImg(src1) console .log(img1) })()
async await 是 promise 的语法糖
async / await 用同步语法来写,取代回调函数写法
async 函数返回的是 Promise 对象
await 相当于 Promise 的 then
try … catch 可以 捕获异常
!!! async 里面 ,await 之前的代码(包括 await )都会立即执行,await 之后的代码,也是等于 Promise then 后的回调代码,会让 Event Loop 分配事件池。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 (async function ( ) { const p4 = Promise .reject('error' ) try { const res = await p4 console .log(res) } catch (err){ console .error(err) } })() (async function ( ) { const p5 = Promise .reject('err1' ) const awaitp5 = await p5 console .log(awaitp5) })()
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 async function fn ( ) { return 100 } (async function ( ) { const a = fn() const b = await fn() })() (async function ( ) { console .log('start' ) const a = await 100 console .log('a' , a) const b = await Promise .resolve(200 ) console .log('b' , b) const c = await Promise .reject(300 ) console .log('c' , c) console .log('end' ) })() async function async1 ( ) { console .log('async1 start' ) await async2() console .log('async1 end' ) } async function async2 ( ) { console .log('async2' ) } console .log('script start' ) async1() console .log('script end' ) async function async1 ( ) { console .log('async1 start' ) await async2() console .log('async1 end' ) } async function async2 ( ) { console .log('async2' ) } console .log('script start' ) setTimeout (function ( ) { console .log('setTimeout' ) }, 0 ) async1() new Promise (function (resolve ) { console .log('promise1' ) resolve() }).then(function ( ) { console .log('promise2' ) }) console .log('script end' )
前端使用异步的场景有哪些? 网络请求,ajax 、图片加载
定时任务,如 setTimeout
1 2 3 4 5 6 7 8 console .log('start' )let img = document .createElement('img' )img.onload = function ( ) { console .log('loaded' ) } img.src = '/xxx.png' console .log('end' )
请描述 event loop (事件循环 / 事件轮询)的机制,可画图 event loop 是异步回调的原理
包含三个概念: callStack: 执行栈。不论是同步还是异步,放进来 callStack 就开始执行 callback Queue: 异步队列。 异步方法都往这里放。 Event Loop:事件轮询器。一个 JS 自带处理事件归属的机制,当 callStack 为空时,Event Loop 开始从 callback Queue 里拿出事件,并放进 callStack里。然后继续等 callStack 处理,执行完又继续轮询。
注意: setTimeout(fn,0)的含义是,指定某个任务在 callStack 最早可得的空闲时间执行,也就是说,尽可能早执行。它在 callback Queue 的尾部添加一个事件,因此要等到 callStack 和 callback Queue 现有的事件都处理完,才会得到执行。
DOM 事件和 Event loop的理解 DOM 事件在没触发时,是存储在哪里? 触发后,存储在 callback Queue 里,通过 Event loop 来处理。 除了异步事件以外,还包括一些用户产生的事件(比如鼠标点击、页面滚动等等)。只要指定过回调函数,这些事件发生时就会进入 callback Queue ,等待主线程读取。
执行顺序: (1)callStack 执行并清空 (2)从 微任务队列 micro task queue 里,获取微任务,执行 (3)在 DOM 有修改的情况下,尝试 DOM 渲染 (4)触发 Event loop
JS 如何执行 从前到后,一行一行执行 如果某行执行报错,则停止下面代码的执行 先执行同步代码,再执行异步
宏任务 和 微任务 分别是什么,区别是什么? 宏任务: setTimeout 、 setInterval 、 Ajax 、 DOM事件 ( 浏览器规定的 ) 微任务: Promise 、 async / await ( ES6 语法 规定的 )
宏任务 是 在 DOM 渲染后 触发 微任务 是 在 DOM 渲染前 触发
微任务执行时机 比 宏任务 要早
promise 和 setTimeout 的执行顺序:(与 宏任务 微任务 有关) 1 2 3 4 5 6 7 8 console .log(100 )setTimeout (() => { console .log(200 ) }) Promise .resolve().then(() => { console .log(300 ) }) console .log(400 )
for … in 和 for … of for … in (以及 forEach for )是常规的同步遍历 for … of 常用于异步的遍历
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 async function plusNum (num ) { return new Promise (resolve => { setTimeout (() => { resolve(num * num) }, 1000 ) }) } const testArr = [1 , 2 , 3 ]testArr.forEach(async (item) => { console .log(await plusNum(item)) }) for await (let i of testArr) { console .log(await plusNum(i)) }
在另一篇博文有详细介绍 异步知识 ,链接:点此 在另一篇博文有详细介绍 JS 执行知识 ,链接:点此
JS Web API 在另一篇博文有详细介绍 JS Web API ,链接:点此
事件 在另一篇博文有详细介绍 事件 ,链接:点此
参考链接:MDN Array-like slice
运行环境 在另一篇博文有详细介绍 运行环境 ,链接 点此