daily-question-02(前端每日一题02)

栏目: 编程语言 · 发布时间: 7年前

内容简介:在这里记录着每天自己遇到的一道印象深刻的前端问题,以及一道生活中随处可见的小问题。强迫自己形成积累的习惯,鞭挞自己不断前行,共同学习。

在这里记录着每天自己遇到的一道印象深刻的前端问题,以及一道生活中随处可见的小问题。

强迫自己形成积累的习惯,鞭挞自己不断前行,共同学习。

Github 地址

2019/04/15 - 2019/04/21

1. 写一个乱序函数 ?

遍历数组元素,然后将当前元素与以后随机位置的元素进行交换。

function shuffle(a) {
    for (let i = a.length; i; i--) {
      let j = Math.floor(Math.random() * i);
      // es6语法
      [a[i - 1], a[j]] = [a[j], a[i - 1]];
    }
    return a;
  }

2. 什么是惰性函数?

惰性函数就是返回一个重写函数。For example:

var foo = function() {
    var t = new Date();
    foo = function() {
      return t;
    };
    return foo();
  };

  foo();

3. 静态作用域与动态作用域 ?

静态作用域 —— 函数的作用域基于函数创建的位置。

动态作用域 —— 函数的作用域基于函数的使用位置。

var value = 1;

function foo() {
  console.log(value);
}

function bar() {
  var value = 2;
  foo();
}

bar(); // 输出 1 。JavaScript 采用的是词法作用域,也称为静态作用域。相同的,动态作用域此代码应该输出 2

4. 手写一个 function call()函数 ?

Function.prototype.call2 = function(context, ...args) {
  // 因为传进来的 context 有可能是 null
  context = context || window;
  // Function.prototype this 为当前运行的函数
  // 让 fn 的上下文为 context
  context.fn = this;

  const result = context.fn(...args);

  delete context.fn;

  return result;
};

5. Vue 组件中的 name 属性的作用 ?

组件在全局用 Vue.component() 注册时,全局 ID 自动作为组件的 name。

指定 name 选项的另一个好处是便于调试。有名字的组件有更友好的警告信息。另外,当在有 vue-devtools,未命名组件将显示成 <AnonymousComponent> ,这很没有语义。通过提供 name 选项,可以获得更有语义信息的组件树。

6. Hash 路由和 History 路由的区别 ?

  1. hash 路由

hash 路由一个明显的标志是带有#,我们主要是通过监听 url 中的 hash 变化来进行路由跳转。( window.addEventListener('hashchange', this.refresh, false); )

hash 的优势就是兼容性更好,在老版 IE 中都有运行,问题在于 url 中一直存在#不够美观,而且 hash 路由更像是 Hack 而非标准,相信随着发展更加标准化的 History API 会逐步蚕食掉 hash 路由的市场。

  1. history 路由

history 路由使用 History API 来实现,具体有:

window.history.back(); // 后退
window.history.forward(); // 前进
window.history.go(-3); // 后退三个页面

history.pushState 用于在浏览历史中添加历史记录, history.replaceState 方法的参数与 pushState 方法一模一样,区别是它修改浏览历史中当前纪录,而非添加记录,同样不触发跳转。

7. Vue 的响应式原理中 Object.defineProperty 有什么缺陷?为什么在 Vue3.0 采用了 Proxy ,抛弃了 Object.defineProperty ?

  1. Object.defineProperty 无法监控到数组下标的变化,导致通过数组下标添加元素,不能实时响应;
  2. Object.defineProperty 只能劫持对象的属性,从而需要对每个对象,每个属性进行遍历,如果,属性值是对象,还需要深度遍历。Proxy 可以劫持整个对象,并返回一个新的对象。
  3. Proxy 不仅可以代理对象,还可以代理数组。还可以代理动态增加的属性。

2019/04/08 - 2019/04/14

4. 写一个“终极类型”判断函数?

function type(obj) {
  var toString = Object.prototype.toString;
  var toType = {};
  var typeArr = [
    "Undefined",
    "Null",
    "Boolean",
    "Number",
    "String",
    "Object",
    "Array",
    "Function",
    "Date",
    "RegExp",
    "Error",
    "Arguments"
  ];
  // 这里利用了object 对象toString() 后 值为 '[object Array]' 等情况进行判断
  typeArr.map(function(item, index) {
    toType["[object " + item + "]"] = item.toLowerCase();
  });

  return typeof obj !== "object" ? typeof obj : toType[toString.call(obj)];
}

2. 写一个函数,判断各种类型的不同变量是否相等,即“终极等于”函数?

const equals = (a, b) => {
  if (a === b) return true;
  // 时间的判断
  if (a instanceof Date && b instanceof Date)
    return a.getTime() === b.getTime();
  // 非 object 类型的判断
  if (!a || !b || (typeof a !== "object" && typeof b !== "object"))
    return a === b;
  if (a.prototype !== b.prototype) return false;
  if (Array.isArray(a) && Array.isArray(b)) a.sort(), b.sort();

  let keys = Object.keys(a);
  if (keys.length !== Object.keys(b).length) return false;
  return keys.every(k => equals(a[k], b[k]));
};

3. mouseover 和 mouseenter 的区别 ?

mouseover:当鼠标移入元素或其子元素都会触发事件,所以有一个重复触发,冒泡的过程。对应的移除事件是 mouseout

mouseenter:当鼠标移除元素本身(不包含元素的子元素)会触发事件,也就是不会冒泡,对应的移除事件是 mouseleave

4. 一句话形容闭包?

闭包就是能够读取其他函数内部变量的函数,或者子函数在外调用,子函数所在的父函数的作用域不会被释放。

一个闭包小栗子:

function f1(){

    n = 999;
  
    function f2(){
  
      console.log(n);
  
    }
  
    return f2;
  
  }
  
  var result = f1();  //返回的是f2函数
  
  result();  //999,读取内部变量

5. js 的 new 操作符做了哪些事情 ?

new 操作符新建了一个空对象,这个对象原型指向构造函数的 prototype,执行构造函数后返回这个对象。

6. 实现一个深拷贝 ?

//所谓深度克隆,就是当对象的某个属性值为object或array的时候,要获得一份copy,而不是直接拿到引用值

function deepClone1(origin, target) {
  //origin是被克隆对象,target是我们获得copy
  var target = target || {}; //定义target
  for (var key in origin) {
    //遍历原对象
    if (origin.hasOwnProperty(key)) {
      if (Array.isArray(origin[key])) {
        //如果是数组
        target[key] = [];
        deepClone1(origin[key], target[key]); //递归
      } else if (typeof origin[key] === "object" && origin[key] !== null) {
        target[key] = {};
        deepClone1(origin[key], target[key]); //递归
      } else {
        target[key] = origin[key];
      }
    }
  }
  return target;
}
  // 第二个function
function deepClone2(data) {
  if (!data || !(data instanceof Object) || typeof data === "function") {
    return data;
  }
  var constructor = data.constructor;
  var result = new constructor();
  for (var key in data) {
    if (data.hasOwnProperty(key)) {
      result[key] = deepClone2(data[key]);
    }
  }
  return result;
}
// 第三个fuction
function deepClone3(origin, target) {
  var target = target || {},
    toStr = Object.prototype.toString;
  for (var prop in origin) {
    if (origin.hasOwnProperty(prop)) {
      //不能把原型链上的一起拷贝了
      //判断是元素类型还是引用类型
      if (typeof origin[prop] == "object" && typeof origin[prop] !== "null") {
        target[prop] = toStr.call(prop) == "[object Array]" ? [] : {};
        arguments.callee(origin[prop], target[prop]); //递归调用
      } else {
        target[prop] = origin[prop]; //原始类型直接复制
      }
    }
  }

  return target;
}
// 第四个function
function deepClone4(obj) {
  //判断是否是简单数据类型,
  if (typeof obj == "object") {
    //复杂数据类型
    var result = obj.constructor == Array ? [] : {};
    for (let i in obj) {
      result[i] =
        typeof obj[i] == "object" && obj[i] !== null
          ? deepClone4(obj[i])
          : obj[i];
    }
  } else {
    //简单数据类型 直接 == 赋值
    var result = obj;
  }
  return result;
}

推荐使用 deepClone2()

7. 函数的防抖与节流 ?

<details>

<summary>点击</summary>

防抖

所谓防抖,就是指触发事件后在 n 秒内函数只能执行一次,如果在 n 秒内又触发了事件,则会重新计算函数执行时间。(防误触)

// 延缓执行
function debounce(func, wait) {
  var timeout;

  return function() {
    var context = this;
    var args = arguments;
    console.log(args);
    console.log(func);
    if (timeout) clearTimeout(timeout);

    timeout = setTimeout(function() {
      func.apply(context, args);
    }, wait);
  };
}
// 立即执行
function debounce(func, wait) {
  var timeout;

  return function() {
    var context = this;
    var args = arguments;

    if (timeout) clearTimeout(timeout);

    var callNow = !timeout;
    timeout = setTimeout(function() {
      timeout = null;
    }, wait);

    if (callNow) func.apply(context, args);
  };
}

节流

所谓节流,就是指连续触发事件但是在 n 秒中只执行一次函数。(限制流量)

// 时间戳
function throttle(func, wait) {
  var previous = 0;

  return function() {
    var now = Date.now();
    var context = this;
    var args = arguments;
    if (now - previous > wait) {
      func.apply(context, args);
      previous = now;
    }
  };
}
// 定时器
function throttle(func, wait) {
  var timeout;

  return function() {
    var context = this;
    var args = arguments;
    if (!timeout) {
      timeout = setTimeout(function() {
        timeout = null;
        func.apply(context, args);
      }, wait);
    }
  };
}

daily-question-02(前端每日一题02)


以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网

查看所有标签

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。

Single Page Web Applications

Single Page Web Applications

Michael Mikowski、Josh Powell / Manning Publications / 2013-9-30 / USD 44.99

Code for most web sites mostly runs on the server. When a user clicks on a link, the site reacts slowly because the browser sends information to the server and the server sends it back again before di......一起来看看 《Single Page Web Applications》 这本书的介绍吧!

随机密码生成器
随机密码生成器

多种字符组合密码

RGB HSV 转换
RGB HSV 转换

RGB HSV 互转工具

RGB CMYK 转换工具
RGB CMYK 转换工具

RGB CMYK 互转工具