[TOC]

答过的笔试题

1. 写出打印的值

1.1 变量定义提升

console.log(a); // 1
function a() {}
var a = 5;
function b() {
  console.log(a); // 2
  var a = 3;
  console.log(a); // 3
}
b();

// 输出结果:
// function a() {}
// undefined
// 3

1、第一个console:全局中,函数声明会提升,变量定义会提升,当发现一个变量已经定义后,便不在提升,所以第一个输出了函数定义。

2、在函数内部,变量定义也会提升到函数内部的顶部,此时的值还是undefined,由于函数内部已经定义变量a,便不会去全局对象中查找,所以输出undefined。

3、变量a已经赋值为3,所以输出3

1.2 箭头函数绑定问题

var c = (v) => {
  console.log(this.name)
  console.log(v)
}
var d = {name: 1}
c.call(d, 2)

// 输出结果:
// undefined // node里面输出是undefined, Chrome终端里面输出的是空
// 2

1.3 var变量for循环输出问题

for(var i = 0; i < 5; i++) {
  setTimeout(function() {
    console.log(i)
  }, 1000)
}
console.log(i)

// 输出结果:
// 5 马上输出
// 5 5 5 5 5 1秒后,输出5个5

上面代码修改成输出1 2 3 4 5

第一个解决办法:在for循环里面定义一个临时变量,然后打印这个临时变量。

for(var i = 0; i < 5; i++) {
  let tmp = i;
  setTimeout(function() {
    console.log(tmp)
  }, 1000)
}
console.log(i)

第二种解决办法:setTimeout方法使用立即执行函数包裹上,传一个变量

for(var i = 0; i < 5; i++) {
  (function(b) {
    setTimeout(function() {
      console.log(b)
    }, 1000)
  })(i)
}
console.log(i)

如果代码最后一行不用输出i,for循环中定义的i使用let来声明,可以实现循环打印1到0.

1.4 async异步函数问题

async function async1() {
   console.log('1')
   await async2()
   console.log('2') 
}
async function async2() {
   console.log('3') 
}
console.log('4')  
setTimeout(() => {
    console.log('5') 
},0)
async1()
new Promise((resolve) => {
    console.log('6') 
    resolve()
}).then(() => {
    console.log('7')  
})
console.log('8')

// 输出结果:
// 4、1、3、6、8、2、7、5

执行过程:

第一步:执行同步:1、输出4、setTimeout回调放入回调队列,2、执行async1(),输出1,执行async2(),输出3,返回一个Promise,async1()函数的await处处于Promise的then,放入回调队列等待执行。3、执行new Promise输出6,then放入回调队列。执行console.log('8'),输出8。总体输出:4、1、3、6、8

第二步:开始去回调队列执行可执行的回调:

是否有可执行的微任务(主要是Promise),有的话取出所有微任务执行,发现有2个,执行async1()中的微任务输出2,执行new Promise()回调,输出7。没有可执行的微任务了,结束微任务。

取出一个宏任务,发现有一个setTimeout,取出执行,输出5.

1.5 setTimeout和async

console.log(1)
setTimeout(() => {console.log('2')},1000) // 第一个setTimeout回调
async function fn() {
   console.log('3');
   setTimeout(() => {console.log('4')},20) // 第二个setTimeout回调
}
async function run() {
   console.log('5');
   await fn()
   console.log('6');
}
run()
var time = (new Date()).getTime()
// 等待150毫秒
while(true) {
    if (time + 150 < (new Date()).getTime()) {
        break;
    }
}
setTimeout(() => {        // 三个setTimeout回调
    console.log('7');
    new Promise((resolve) => {
        console.log('8') 
        resolve()
    }).then(() => { console.log('9') })
},0)
console.log('10')

// 输出结果:
// 1、5、3、10、6、4、7、8、9、2

执行过程:

1、执行一个宏任务(同步任务): 执行console输出1,执行run()异步函数,执行console输出5,执行函数fn(),执行console输出3,一个setTimeout放入回调队列,fn()函数执行完毕,await等待到一个Promise回调,放入到回调队列,run()执行完毕,等待状态。while循环,消耗了150毫秒,接着执行,遇到一个setTimeout放到回调队列,执行console输出10.

第一个宏任务执行结束,输出:1、5、3、10。此时回调队列有3个setTimeout回调。

2、查看是否有微任务可执行,经查找,没有,忽略。

3、执行一个宏任务,有一个setTimeout可执行,执行console输出4。

4、查看是否有微任务,没有,执行一个宏任务,发现有一个可执行的,执行:执行console输出7,执行new Promise,执行console输出8,一个微任务放到回调队列。

5、查看是否有微任务可执行,经查找有一个可以执行,执行微任务:执行console输出9。

6、执行一个宏任务,有一个可以执行了,执行console输出2.

7、执行结束。

1.6 let变量不会绑定到window上

let obj = {
  foo: function() {
    console.log(this.bar)
  },
  bar: 1
}
let foo = obj.foo;
let bar = 2;
obj.foo() // 1
foo() // undefined

由于foo方法中输出this.bar,全局中,使用let声明的bar不会绑定到window上面,所以单独调用foo方法中,输出undefined

2. 写出几个vue或react常用的声明周期狗子函数

  • 挂载阶段

    constructor:初始化构造函数

    static getDerivedStateFromProps:调用 render 方法之前调用,并且在初始挂载及后续更新时都会被调用

    render():渲染的html元素,react类组件唯一必须实现的方法

    componentDidMount():会在组件挂载后(插入 DOM 树中)立即调用

  • 更新阶段

    getDerivedStateFromProps():上面有说明

    shouldComponentUpdate():props或state变化,render()渲染之前调用,返回默认true(需要渲染页面)

    render():上面有说明

    getSnapshotBeforeUpdate():在render()之后,渲染输出(提交到DOM节点)之前调用

    componentDidUpdate(): 更新后调用,首次渲染不会执行此方法

  • 卸载阶段

    componentWillUnmount():组件卸载及销毁之前直接调用

3. 写一个递归方法,把数组[1,2,[3,[4,5],6],7],变成一维数组[1,2,3,4,5,6,7]

function func(arr) {
    var newArr = []
    for(var i = 0; i < arr.length; i++) {
        if (Array.isArray(arr[i])) {
            newArr = newArr.concat(func(arr[i]))
        } else {
            newArr.push(arr[i])
        }
    }
    return newArr
}
var arr = [1,2,[3,[4,5],6],7];
var newArr = func(arr)
console.log('newArr:', newArr)

4. 字符串相邻字符去重aaabbdebb=>abdeb

给一个字符串,把字符串中相邻重复的字符去掉,比如aaabbdebb 变成abdeb

var str = 'aaabbdebb'
function deleteRepeat(str) {
  var newStr = str[0]
  var tmp = str[0]
  for (var i = 1; i < str.length; i++) {
    if (tmp !== str[i]) {
      newStr += str[i]
      tmp = str[i]
    }
  }
  return newStr;
}
var t = deleteRepeat(str);
console.log(t)

5. get1_by2_list334_android7删除偶数和_

给一个字符串:get1_by2_list334_android7(每个单词后偶棉携带一个数字),删除其中偶数和_,不用不用循环,只用正则,如何实现输出get1bylistandroid7

var str = 'get1_by2_list334_android7';
var result = str.replace(/(\d*[02468])*_*/g, '')
console.log(result);

6.不用循环语句和迭代器实现0到1000数组赋值

不使用任何循环控制语句和迭代器的情况下,实现一个0到1000的数组赋值。

var i = 0;
var arr = [];
function fn() { 
  arr[i] = i
  i++;
  if (i > 1000) {
    clearInterval(id)
    console.log(arr)
  }
}
var id = setInterval (fn, 0);

7. JSON格式的树状结构生成一个dom树

将类似以下JSON表示的树状结构(可以无限层级)通过parseDOM函数(使用document.createElement,document.createTextNode,appendChild等方法)生成一颗DOM树(返回一个element元素)

const jsonTree = {
    "tagName": "ul",
    "props": {
      "className": "list",
      "data-name": "jsontree"
    },
    "children": [{
        "tagName": "li",
        "children": [{
          "tagName": "img",
          "props": {
            "src": "//img.alicdn.com/tps/TB1HwXxLpXXXXchapXXXXXXXXXX-32-32.ico",
            "width": "16px"
          }
        }]
      },
      {
        "tagName": "li",
        "children": [{
          "tagName": "a",
          "props": {
            "href": "https://www.aliyun.com",
            "target": "_blank"
          },
          "children": "阿里云"
        }]
      }
    ]
  };

  function parseDOM(jsontree){
    const {tagName,props,children} = jsontree;
    const element = document.createElement(tagName);
    // 添加属性
    if (typeof props === 'object') {
      for (var key in props) {
        element.setAttribute(key, props[key]);
      }
    }
    // 添加子元素
    if (children) {
      if (typeof children === 'string') {
        element.innerHTML = children;
      } else if (Array.isArray(children)) {
        children.forEach(function(e) {
          element.appendChild(parseDOM(e))
        })
      }
    }
    return element;
  }
  var one = document.getElementById('one');
  one.appendChild(parseDOM(jsonTree));

http://js.jirengu.com/juyeki/edit?html,js,output

8. ['1', '2', '3'].map(parseInt)结果是啥?为什么?

此面试题考察的内容:1、对于数组map函数的理解以及使用。2、对于parseInt的转换规则是否理解。

数组的map方法接受一个函数作为参数。该函数调用时,map方法向它传入三个参数:当前成员、当前位置和数组本身。

parseInt(string, radix) :函数将其第一个参数转换为一个字符串,对该字符串进行解析,然后返回指定基数的十进制整数或 NaNradix 是2-36之间的整数,表示被解析字符串的基数。第二个参数可不传,默认值为10,即默认是十进制转十进制。第二个参数若传递的是0、NaN、undefined,则分两种情况: 1、若string以“0x”或“0X”开头,则默认为16进制。2、若不是第一种情况则会10进制

[“1”,“2”,“3”].map(parseInt)解析:

parseInt("1",0),
parseInt("2",1),
parseInt("3",2),

1、parseInt(“1”,0)由于传入的radix是0,而string非“0x”或“0X”则进制为十进制,parseInt(“1”,0)则为parseInt(“1”,10),输出1
2、parseInt(“2”,1)由于radix是1,不在2-36区间,输出NaN。
3、parseInt(“3”,2)的radix为2,在2-36区间,又因为在二进制中“3”是不存在的,为无效数字,输出NaN

所以最终结果:

[1,NaN,NaN]
powered by Gitbook文件最后修改时间: 2024-09-19 22:59:45

results matching ""

    No results matching ""