面试题

1. setTimeout()相关知识

上题,涉及到的知识点:函数的执行顺序闭包块级作用域等。

for (var i = 1; i <= 5; i++) {
  setTimeout( function timer() {
      console.log(i);
  }, i * 1000 );
}
//输出5个数字6,要求改动上述代码,使其依次输出1、2、3、4、5

在我们学习setTimeout的时候就知道,setTimeout有两个参数,第一个参数是回调函数,第二个参数是毫秒数,表示要执行回调函数所要延迟的时间。

但我们还需要知道的是,setTimeout会返回一个Id,即这个定时器的Id,在上面的代码中其实已经创建了5个定时器,但是默认只返回了最后的一个Id,我们可以通过将Id赋值给一个变量,来看到这个过程。由于方法里面没有return任何东西出来,所以返回值为undefined。通过过这个定时器的Id,可以使用clearTimeout(id)方法清除掉这个定时器,这里就不再赘述了。

setTimeout()的毫秒数设置为0的时候,仍然是先执行完函数调用栈中的代码,然后立即调用定时器。这是因为,我们的定时器都被放在了一个被称为队列的数据结构中,等待上下文的可执行代码运行完毕后,才开始运行定时器,也就是定时器才刚开始计时。

所以在定时器的方法执行的时候,变量i已经变成了6,所以输出的全部是6。因为5个定时器所打印出来的是同一个i变量,所以想要实现输出不同的数字,就需要把每个定时器所访问的变量独立起来,这就用到了JavaScript的闭包。

for (var i = 1; i <= 5; i++) {
    (function(i){
        setTimeout( function timer() {
              console.log(i);
          }, i * 1000 );
    })(i);
}
  
//上面的代码是标准答案,将变量i作为参数传到闭包中
//我们也可以通过作用域在函数内部把变量隔离起来
//其实,在闭包内部访问i的时候,i就是一个常量
for (var i = 1; i <= 5; i++) {
    (function(){
        var s = i;//把i赋值给另外一个变量
        setTimeout( function timer() {
              console.log(s);
          }, s * 1000 );
    })();
}

//当然,也可以把setTimeout的回调函数做成一个闭包,同样能得到正确的结果。

块级作用域--关键字let

在ES6中提出了一个新的关键字let,就可以声明一个仅对当前“{}”内部有作用的变量。

2. js中的this指向

上题:

var a=1;
function f1(){
	var a=2;
	console.log("this1",this)
	console.log(this.a+a)
}
function f2(){
	var a=3;
	console.log("this2",this)
	f1();
}
f2();
//this2 window
//this1 window
//3  (1+2)
  1. 全局作用域或者普通函数中this指向全局对象window。
/直接打印
console.log(this) //window

//function声明函数
function bar () {console.log(this)}
bar() //window

//function声明函数赋给变量
var bar = function () {console.log(this)}
bar() //window

//自执行函数
(function () {console.log(this)})(); //window

2.  方法调用中谁调用this指向谁

//对象方法调用
var person = {
    run: function () {console.log(this)}
}
person.run() // person

//事件绑定
var btn = document.querySelector("button")
btn.onclick = function () {
    console.log(this) // btn
}
//事件监听
var btn = document.querySelector("button")
btn.addEventListener('click', function () {
   console.log(this) //btn
})

//jquery的ajax
 $.ajax({
    self: this,
    type:"get",
    url: url,
    async:true,
    success: function (res) {
        console.log(this) // this指向传入$.ajxa()中的对象
        console.log(self) // window
    }
   });
 //这里说明以下,将代码简写为$.ajax(obj) ,this指向obj,在obj中this指向window,因为在在success方法中,独享obj调用自己,所以this指向obj

3. 在构造函数或者构造函数原型对象中this指向构造函数的实例

3. 写一个函数,实现一个简单的模板引擎:

let template = '我是{{name}},年龄{{age}},性别{{sex}}';
let data = {
  name: '姓名',
  age: 18
}
render(template, data); // 我是姓名,年龄18,性别undefined

答案:

简单实现

function render(template, data) {
  const reg = /\{\{(\w+)\}\}/; // 模板字符串正则
  if (reg.test(template)) { // 判断模板里是否有模板字符串
    const name = reg.exec(template)[1]; // 查找当前模板里第一个模板字符串的字段
    template = template.replace(reg, data[name]); // 将第一个模板字符串渲染
    return render(template, data); // 递归的渲染并返回渲染后的结构
  }
  return template; // 如果模板没有模板字符串直接返回
}

一行代码实现方式

function render(template, data) {
  return template.replace(new RegExp('{{(.*?)}}', 'g'), (match, key) => data[key.trim()]);
}

(.*?) 解释:

.*具有贪婪的性质,首先匹配到不能匹配为止,根据后面的正则表达式,会进行回溯。
.*?则相反,一个匹配以后,就往下进行,所以不会进行回溯,具有最小匹配的性质。

关于正则:https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/RegExp

4. 合并有序数组:

let arr= [[1, 2], [0, 3, 4,4,4,6,7,8,9,10], [-1, 4],[-1,3],[-1],[100,200],[5,1000,30000]];

我们希望将上述数组合并为一个有序数组,怎么处理呢?

最简单的方案就是:将数组整体合并,然后sort排序,代码如下:

let ret=arr.reduce((arr1,arr2)=>arr1.concat(arr2)).sort((a,b)=>a-b);
ret=Array.from(new Set(ret));
console.log(ret);

但是上面的代码没有充分利用数组子元素本身就是有序数组这一特性,

我们利用“归并排序”算法,可以大大的提高类似数组的合并排序性能,代码(代码里面有详细注释)如下:

var arr1=[1,3,6,7,8];  var arr2=[2,4,5,9] 两个有序数组排序,归并排序:

function mergeArray(arr1,arr2){
    var ind1=0; //标记arr1的对比元素的初始索引值
    var ind2=0; //标记arr2的对比元素的初始索引值
    var arr=[]; //作为输出的新数组
    while(ind1<arr1.length && ind2<arr2.length){ //当arr1和arr2元素均未全部存入arr中,则从第一个元素开始进行比较,将较小的那个元素存入arr
        if(arr1[ind1]<=arr2[ind2]){
            arr.push(arr1[ind1]); //若arr1的对比元素小于arr2的对比元素,则将arr1的对比元素存入arr中
            ind1++;
        }else{
            arr.push(arr2[ind2]);
            ind2++;
        }
    }
    while(ind1<arr1.length){ //当arr2的元素已全部存入arr中,则直接将arr1剩余的所有元素依次存入arr
        arr.push(arr1[ind1]);
        ind1++;
    }
    while(ind2<arr2.length){ //当arr1的元素已全部存入arr中,则直接将arr2剩余的所有元素依次存入arr
        arr.push(arr2[ind2]);
        ind2++;
    }
    return arr;
}

5. alert第几个li

<ul id="container">
    <li>第一个</li>
    <li>第二个</li>
    <li>第三个</li>
</ul>
$("ul li").click(function () {
    var index = $("ul li").index(this);
    alert(index);
 });

6. 实现bind

Function.prototype.bind2 = function(context){
    var that = this;
    return function(){
        that.apply(context)
    }
}

7. 二叉树翻转

初始值判断+递归

   function Mirror(TreeNode root) {
        if(root == null) {
            return;
        }
        if((root.left == null) && (root.right == null)) {
            return;
        }
        TreeNode temp = root.left;
        root.left = root.right;
        root.right = temp;
        Mirror(root.left);
        Mirror(root.right);
   }

8.  A继承B

A.prototype = Object.create(B.prototype);

var a= new A()

  1. var a = {};

  2. a.__proto__ = A.prototype;

  3. A.call(a)

new就是该对象的 _ proto _属性指向该类的prototype 


而Object.create就直接指向显示的指向

9. 解析 URL Params 为对象

尽可能的全面正确的解析一个任意 url 的所有参数为 Object,注意边界条件的处理。

let url = 'http://www.domain.com/?user=anonymous&id=123&id=456&city=%E5%8C%97%E4%BA%AC&enabled';
parseParam(url)
/* 结果
{ user: 'anonymous',
  id: [ 123, 456 ], // 重复出现的 key 要组装成数组,能被转成数字的就转成数字类型
  city: '北京', // 中文需解码
  enabled: true, // 未指定值得 key 约定为 true
}
*/

答:

function parseParam(url) {
  const paramsStr = /.+\?(.+)$/.exec(url)[1]; // 将 ? 后面的字符串取出来
  const paramsArr = paramsStr.split('&'); // 将字符串以 & 分割后存到数组中
  let paramsObj = {};
  // 将 params 存到对象中
  paramsArr.forEach(param => {
    if (/=/.test(param)) { // 处理有 value 的参数
      let [key, val] = param.split('='); // 分割 key 和 value
      val = decodeURIComponent(val); // 解码
      val = /^\d+$/.test(val) ? parseFloat(val) : val; // 判断是否转为数字

      if (paramsObj.hasOwnProperty(key)) { // 如果对象有 key,则添加一个值
        paramsObj[key] = [].concat(paramsObj[key], val);
      } else { // 如果对象没有这个 key,创建 key 并设置值
        paramsObj[key] = val;
      }
    } else { // 处理没有 value 的参数
      paramsObj[param] = true;
    }
  })

  return paramsObj;
}

10. 利用CSS实现一个元素旋转另一个元素的功能,如下图,1围绕2旋转

知识点:

1. 旋转transform:rotate(360deg);

2. 旋转原点,transform-origin: -15px 15px;元素坐标轴原点位于 元素左上角,XY轴方向 向右向下

3. 设置过渡 tranlate:transform 5s;

4. css选择器 兄弟元素 使用 “+” 号

<!doctype html>
<html>
<head>
<meta charset="utf-8">
<meta name="author" content="http://www.softwhy.com/" />
<title>元素旋转</title>
<style type="text/css">
#box {
  margin:200px;
  font-size:0px;
}
.aa,.bb {
  width: 30px;
  height: 30px;
  display: inline-block;
  border: 1px solid #000;
  border-radius: 100%;
  margin-top: 50px;
  text-align: center;
  line-height: 30px;
  font-size:12px;
}
.bb:hover + div  {
  color: #f00;
  transform: rotate(360deg);
  transform-origin: -15px 15px;
  transition:transform 5s;
}
</style>
</head>
<body>
<div id="box">
  <div class="bb">2</div>
  <div class="aa">1</div>
</div>
</body>
</html>

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值