0120面试题——async、类型转换、this指向、作用域

204 阅读4分钟

javascript

模拟实现

  • 根据async/await与generator的关系模拟实现
function myAsync(genF){
    return new Promise(function(resolve, reject) { // async返回的是一个promise对象
        const gen = genF(); // 拿到这个遍历器
        function step(nextF) {
          let next;
          try {
            next = nextF(); // 执行传入的回调函数获取{value:xx,done:xx}
          } catch(e) {// 出错就直接抛出,抛出的错误就是当前出错的promise对象
            return reject(e);
          }
          if(next.done) {//完成就直接resolve
            // resolve的参数如果是promise对象,会继续等待promise对象返回,再resolve返回的结果
            return resolve(next.value);
          }
          // 直接resolve当前的指针指向的对象然后继续执行下一个
          Promise.resolve(next.value).then((res)=> {
            step(()=> gen.next(res)); // 返回上一个promise对象的返回值
          }).catch((e)=>{
            step(()=> gen.throw(e)); // 错误就抛出
          })
        }
        // 初始化调用这个遍历器
        step(()=> gen.next());
      });
}


// 测试代码
myAsync(function* () {
    const a = yield Promise.resolve(1)
    const b = yield new Promise((res, rej) => {
        setTimeout(() => {
            res(2)
        }, 2000)
    })
    const c = yield Promise.resolve(3)
    console.log(a, b, c);

    try {
        const d = yield Promise.reject(4)
    } catch (error) {
        console.log(error);
    }

    return [a, b, c]
}).then(console.log)
// 输出
// 1 2 3
// 4
// [1,2,3]

理论

  • 考察隐式类型转换,下面if为真的有哪些
if([])		//  [] 对象为true		// 只有'', 0, false, undefined, null, NaN 在转换为布尔值是变成false
if({})		//  {} 对象为true
if([]==false)	//  转换后比较为true		// 类型不同的值进行比较时先转为同类型再比较,对象先调用valueOf取值,如果仍是对象再取toString的值,再将 字符串转换成逻辑值,因此空数组转为字符串为'',所以逻辑值为false,比较结果为true
if({}==false)	//转换后比较为false		// {} 依次调用valueOf和toString,得到的字符串为[object Object],转换为布尔值为true,比较的结果为false
  • this指向考察
function a(){
    this.b = 3
}
a()
console.log(b) // 3,a中的this为window,影响了全局的b
var b = 5
console.log(b) // 5, 前一句代码修改了全局的b
var c = new a()
console.log(b) // 5, 前一句代码创建a的实例时,对b的修改影响的是新创建的对象
c.prototype.b = 4
c.prototype.c = 5
console.log(c.b) // 3,从当前对象到原型链末端依次查找b,取距离最近的,即自身的b
console.log(c.c) // 5,当前对象没有属性c,因此取值为原型上的c
console.log(b) // 5, 始终没有代码对全局的b再次进行修改
  • 什么是动态作用域?什么是静态作用域?
    • 动态作用域:函数的作用域需要在运行过程中才能动态确定
    • 静态作用域:函数的作用域在定义时就已经确定
  • js是动态还是静态作用域
    • 静态作用域
  • 作用域考察
1
var scope = "global scope";
 function checkscope() {
     var scope = "local scope";
     function f() {
         return scope;
     }
     return f();
 }
 console.log(checkscope()); // local scope2
 var scope = "global scope";
 function checkscope(){
     var scope = "local scope";
     function f(){
         return scope;
     }
     return f;
 }
 checkscope()(); // local scope3
for(var i = 0;i<2;i++){
    setTimeout(()=>{
        for(var j = 0;j<3;j++){
            setTimeout(()=>{
                console.log(i*j)
            },0)
        }        
    },0)
}
// 输出结果是多少?为什么
/**
* 答:输出结果是666666,因为setTimeout是异步代码,外层的setTimeout要等外部的for循环完成才会执行,执行时i = 2,
* 内部的setTimeout要等内部的for循环完成才会执行,执行时j = 3,因为var声明的变量没有块级作用域,ij全都定义在外部,内层定时器回调函数引用的变量也都是相同的,因此全都打印的2*3,即6
**/
// var 变为 let 结果又是多少?为什么
/**
* 答:输出结果是000012,同样内外层的setTimeout的执行需要等到各自的for执行完成,但是内层定时器回调引用的变量是各自的,原因如下:
* let声明的变量拥有块级作用域,在for循环中这个块级作用域的范围就存在于for右边的一对小括号里面,因此每次循环的时候都会创建一个不同的变量,
* 并且将之前的变量值赋值过去。而由于内层定时器回调函数的引用,使得这些块级作用域的变量生存期被延长了,存在于对应回调[[scoped]]的block对象中
*/

dom相关

  • 如何获取一个dom对象
document.getElementById(id)
document.getElementsByClassName(className)
document.getElementsByName(name)
document.getElementsByTagName(tagName)
document.querySelector(selector)  
document.querySelectorAll(selector)
  • 如何获取指定dom的指定属性
// 获取一般属性
.getAttribute()
// 获取自定义属性
.dataset
  • 如何获取指定dom的指定样式
.style[property]
  • 如何获取指定dom的生效样式
getComputedStyle(element)
// 老版ie
element.currentStyle