关于javascript:js面试相关

6次阅读

共计 10208 个字符,预计需要花费 26 分钟才能阅读完成。

具体内容能够查看笔记

1、节流 throttle 和 防抖 debounce

防抖和节流的作用:当某一个操作被用户频繁触发时,例如 window 的 onresize,input 的 oninput 等,应用防抖和节流能无效管制 callback 的触发频率
应用形式:input.addEventListenter(“input”,debounce(handle,delay))

防抖

const debounce = (callback,delay)=>{
    let timer = null
    return function(){if(timer != null) clearTimeout(timer)
        timer = setTimeout(()=>{callback.apply(this,arguments)},delay)
    }
}

节流

形式(1)

const throttle = (callback,delay)=>{let prev = Date.now()
    return function (){let now = Date.now()
        if(now-prev>delay){callback.apply(this,arguments)
            prev = Date.now()}
    }
}

缺点:必须期待 wai 工夫能力首次点击,因为 throttle()初始化了 prev 工夫,若此时点击,则 now-prev<wait,不会执行 callback。并且,进行点击后,不肯定会触发之前的 callback

形式(2)

const throttle = (callback,delay)=>{
    let timer = null
    return function(){if(!timer){setTimeout(()=>{callback.apply(this,arguments)
                timer = null
            },delay)
        }
    }
}

缺点:首次点击必然会执行,但要期待 wai 工夫才有成果,因为是 setTimeout 定时器,并且在定时器正在执行中的时候,因为 timer=null,此时间断触发的话,会产生多个定时器

综合两者

const throttle = (callback, delay) => {let prev = Date.now()
    let timer = null
    return function () {let now = Date.now()
        let surplus = delay - (now - prev)
        clearTimeout(timer)
        if (surplus <= 0) {callback.apply(this, arguments)
                prev = Date.now()} else {timer = setTimeout(() => {callback.apply(this, arguments)
            }, surplus)
        }
    }
}

2、获取元素行间款式

dom.currentStyle.left || getComputedStyle(dom,null).left

备注:getComputedStyle 第二个参数,用来获取伪元素的属性,例如 getComputedStyle(dom,”after”).width

3、元素地位 offsetWidth 等属性

一个元素 div,通常有 width,padding,margin,border

dom.clientWidth : width + padding
dom.offsetWidth : width + padding + border

dom.offsetLeft : 绝对于定位父级 (position:relative) 的 left 值
dom.offsetParent : 定位父级元素(通常状况下,要 dom.offsetParent.tagName 去拿到父级的 tagName)

height 与 width 雷同

4、节流优化 resize

(function () {var throttle = function (type, name, obj) {
        obj = obj || window;
           var running = false;
           var func = function () {if (running) {return;}
               running = true;
               requestAnimationFrame(function () {obj.dispatchEvent(new CustomEvent(name));
                   running = false;
               });
           };
           obj.addEventListener(type, func);
    };
    throttle("resize", "optimizedResize");
})();
window.addEventListener("optimizedResize", ()=>{});

5、原型、原型链

如果看不到图片:请先点击参考链接再回到本页面刷新就有图片了

原型的概念
每个 javascript 对象(null 除外)创立的时候,就会给它关联一个对象,这个对象就是咱们说的原型,每个对象都会从原型中继承属性。

(1)每个函数都有 prototype,指向函数的原型(这玩意就是函数的原型),原型也是一个对象

(2)每个对象都有__proto__,对象的原型(用来连贯实例和构造函数),值为:构造函数的 prototype。(构造函数的 prototype,不就是构造函数的原型嘛,所以这玩意也是构造函数的原型)

function Parent(){}
const parent1 = new Parent()
parent1.__proto__ = Parent.prototype

(3)constructor:每个原型都有一个 constructor 属性,指向关联的构造函数

function Parent(){} // 构造函数
Parent.prototyoe.constructor = Parent //Parent.prototype 是构造函数的原型

之前有一个误区:

function Person(){}
const person1 = new Person()
console.log(person1.constructor) //Person

其实 person1 并没有 constructor 这个属性,因而,去它的原型__proto__下面找,也就是构造函数 Person.prototype 下面找。正好 Person.prototype.constructor = Person

(4)instanceof:判断是否是实例

function A() {}
const a = new A()
console.log(a instanceof A) //true
a.__proto__ = {}
  console.log(a instanceof A) //false

从这个例子能够看出:a 和 A 要在同一个原型链上,a instanceof A 才为 true,扭转了 a 的原型__prpto__之后,就为 false 了

原型链
之前说了,原型(构造函数的 prototype)也是一个对象。既然是对象,它也有原型。

原型的构造函数是 Object

因而,(构造函数.prototype).__prpto__ = Object.prototype // (构造函数.prototype)是一个对象

最初:js 规定,Object.prototype 的原型指向 null。
因而原型链如下图

例题

Function.prototype.a = 1;
Object.prototype.b = 2;

function A() {}
var a = new A();

console.log(A.a, A.b); // 1 2
console.log(a.a, a.b); // undefined 2
console.log(Function.b, Object.a); // 2 1

6、手写一个 new 函数

new 次要实现了 4 个操作:
(1)创立一个空对象
(2)链接原型
(3)绑定 this
(4)返回一个对象

function create() {
  const args = arguments
  const obj = new Object() // 翻新空对象
  const cradleFn = [].shift.call(args) // 拿到构造函数
  obj.__proto__ = cradleFn.prototype // 链接原型
  const result = cradleFn.apply(obj, args) // 绑定 this
  return (typeof result == "object" ? result : obj)
}

7、new 构造函数返回值问题

new 总是会返回对象,如果构造函数外面有 return {},则返回这个{},否则返回 this 实例

function A(){return 123}
const a = new A() // a 为{}

function B(){return {name:123}
}
const b = new B() // b 为{name:123}

8、如何判断一个函数是否由 new 调用

(1)instanceof

function A(){if(this instanceof A){}}(2)new.target

function B(){if(new.target === B){}}

9、继承

(1)call 办法

function Parent(){
    this.name = "zhang"
    this.age = 35
}
function Child(){Parent.call(this)
    this.age = 15
}
const son = new Child()
console.log(son.name) //zhang
console.log(son.age)  //15

毛病:无奈继承 Parent.prototype 上的值

(2)原型

function Parent(){
    this.name = "zhang"
    this.arr = [1,2,3]
}
function Child(){this.age = 15}
Child.prototype = new Parent()

毛病:this.arr 是专用的,一旦其中一个实例更改了 arr,其余实例的 arr 属性也会更改

(3)call、原型组合形式

function Parent(){this.arr = [1,2,3]
    this.name = "zhang"
}
function Child(){Parent.call(this)
}
Child.prototype = Object.create(Parent.prototype) // 关键点

10、call、apply 惯例应用

Math.max.apply(null,[2,5,3,7,1])
Array.prototype.slice.call({1:”zhang”,2:true,length:5}) // 类数组(有 length 属性),转真正数组

11、浏览器同源策略

软大神的具体文档

同源:协定 (http)、域名(www.xx.com)、端口号(:8080) 必须都雷同
阮大神说:
非同源时,次要三种行为受到限制
(1)Cookie、LocalStorage 和 IndexDB 无奈读取
(2)DOM 无奈取得
(3)AJAX 申请不能发送

12、回流(重排)与重绘

参考资料

回流:当浏览器必须重新处理和绘制局部或全副页面时,回流就会产生。我了解为,浏览器依据 renderTree 计算出元素盒子的几何信息(地位、大小)
重绘:依据回流计算的几何信息,转换成理论像素

何时触发回流:

  • 增加或删除可见的 DOM 元素
  • 元素的地位发生变化
  • 元素的尺寸发生变化(包含外边距、内边框、边框大小、高度和宽度等)
  • 内容发生变化,比方文本变动或图片被另一个不同尺寸的图片所代替
  • 页面一开始渲染的时候(这必定防止不了)
  • 浏览器的窗口尺寸变动(因为回流是依据视口的大小来计算元素的地位和大小的)
    留神:回流肯定会触发重绘,而重绘不肯定会回流

获取哪些属性会触发回流重绘:

  • offsetTop、offsetLeft、offsetWidth、offsetHeight
  • scrollTop、scrollLeft、scrollWidth、scrollHeight
  • clientTop、clientLeft、clientWidth、clientHeight
  • getComputedStyle
  • getBoundingClientRect

如何缩小回流重绘:
1、应用 style.cssText 和 className

  • el.style.cssText += ‘border-left: 1px; border-right: 2px; padding: 5px;’;
  • el.className += ‘ active’;

2、批量批改 DOM
(1)使元素脱离文档流
(2)对其进行屡次批改
(3)将元素带回到文档中

3、防止触发同步布局事件(循环中获取触发回流重绘的属性)

function initP() {for (let i = 0; i < paragraphs.length; i++) {paragraphs[i].style.width = box.offsetWidth + 'px';
    }
}

4、css3 硬件加速

  • transform
  • opacity
  • filters

13、try{…}catch 捕捉谬误

(1)之前

try{a}catch(e){}

无奈捕捉谬误:a. 在语法检测时就出错了,此时主线程尚未执行 try
(2)之中

try{a.b}catch(e){}

能够捕捉:a.b 未定义
(3)之后

try{setTimeout(()=>{a.b},100)
}catch(e){}

无奈捕捉:try 曾经执行实现了

总结:能被 try catch 捕捉到的异样,必须是在报错的时候,线程执行曾经进入 try catch 代码块,且处在 try catch 外面,这个时候能力被捕捉到。

14、执行上下文、作用域、作用域链

执行上下文:执行上下文是评估和执行 JavaScript 代码的环境的抽象概念,每当 Javascript 代码在运行的时候,它都是在执行上下文中运行(了解:全局 js 代码或者函数在执行之前,会先做两件事:绑定 this,同时创立一个蕴含变量和函数的环境)

执行上下文参考文档

作用域:存储和拜访变量的区域
作用域链:函数执行上下文内,拜访变量时,若不存在,就拜访该函数内部的执行上下文,若还不存在,就持续拜访内部的上下文,始终到全局的执行上下文,这样就造成了一个作用域链

15、for in,Object.keys,Object.getPropertyNames 之间的区别

function Parent() {}
Parent.color = "salmon"
Parent.prototype.pro = "pro"

const obj = new Parent()
obj.name = "zhang"

Object.defineProperties(obj, {
  gender: {
    value: "male",
    enumerable: false
  }
})

for (var key in obj) {console.log("key=", key, "value=", obj[key])
}
// key= name value= zhang
// key= pro value= pro

console.log(Object.keys(obj)) //["name"]
console.log(Object.getOwnPropertyNames(obj)) //["name","gender"]

for in 能够拜访本身和原型链上的属性
Object.keys 只能拜访本身可枚举的属性
Object.getOwnPropertyNames 能拜访本身所有属性(可枚举和不可枚举)

16、EventLoop 事件循环、宏工作、微工作

参考文档讲得通俗易懂

17、TCP/IP 三次挥手、四次握手

参考文档
http1.0、2.0、https 参考文档

18、强缓存 vs 协商缓存

参考文档

19、浏览器输出 url 之后产生了什么

参考文档

20、算法

(1)冒泡算法

  function bubbleSort(arr) {if (!arr || !arr.length) return
    for (let i = 0; i < arr.length - 1; i++) {for (let j = 0; j < arr.length - 1 - i; j++) {if (arr[j] > arr[j + 1]) {[arr[j], arr[j + 1]] = [arr[j + 1], arr[j]]
        }
      }
    }
    return arr
  }

(2)抉择排序

  function selectSort(arr) {if (!arr || !arr.length) return
    for (let i = 0; i < arr.length; i++) {for (let j = i; j < arr.length; j++) { // 这里要留神:j = i
        if (arr[j] < arr[i]) {[arr[j], arr[i]] = [arr[i], arr[j]]
        }
      }
    }
    return arr
  }

(3)插入排序

  Array.prototype.insertSort = function () {let arr = [].slice.call(this)
    for (let i = 1; i < arr.length; i++) { // 留神,这里是 i =1
      for (let j = i; j > 0; j--) { // 留神,这里是倒序
        if (arr[j] < arr[j - 1]) {[arr[j], arr[j - 1]] = [arr[j - 1], arr[j]]
        } else {break}
      }
    }
    return arr
  }

(4)疾速排序

  Array.prototype.quickSort = function () {const arr = [].slice.call(this)
    if (arr.length < 1) return arr // 递归进口
    let left = [],
      right = [],
      cur = arr.splice(0, 1)
    for (let i = 0; i < arr.length; i++) {if (arr[i] < cur) {left.push(arr[i])
      } else {right.push(arr[i])
      }
    }
    return [].quickSort.call(left).concat(cur, [].quickSort.call(right))
  }

21、堆栈、队列

堆栈:后进先出
队列:先到先得

22、模仿 call、apply、bind

call 办法:

  Function.prototype.callFn = function (context) {
    context = context || window
    context.fn = this
    var args = []
    for (var i = 1; i < arguments.length; i++) {args.push("arguments[" + i + "]")
    }
    var result = eval("context.fn(" + args + ")")
    delete context.fn
    return result
  }

apply 办法

  Function.prototype.applyFn = function (context, arr = []) {
    context = context || window
    context.fn = this
    var args = []
    for (var i = 0; i < arr.length; i++) {args.push("arr[" + i + "]")
    }
    var result = eval("context.fn(" + args + ")")
    delete context.fn
    return result
  }

bind 办法

  Function.prototype.bindFn = function (context) {if (typeof this != "function") {throw new Error("必须是函数")
    }
    var outerArgs = [].shift.call(arguments)
    var self = this
    var resultFn = function () {var innerArgs = [].slice.call(arguments)
      return self.apply(context, outerArgs.concat(innerArgs))
    }
    return resultFn
  }

23、深拷贝

1、递归:如果数据深度太深,会造成栈溢出

  const deepClone = (source) => {const result = Array.isArray(source) ? [] : {}
    for (let i in source) {if (typeof source[i] == "object") {result[i] = deepClone(source[i])
      } else {result[i] = source[i]
      }
    }
    return result
  }

2、循环:只是减少了数据广度,不会造成栈溢出

  function catchType(obj) {const type = Object.prototype.toString.call(obj)
    return type.substring(8, type.length - 1).toLowerCase()}
  function deepCloneStack(obj) {const root = {}
    const stack = [{
      parent: root,
      data: obj
    }]
    while (stack.length) {const pop = stack.pop()
      const {
        parent,
        data
      } = pop
      for (let i in data) {if (data.hasOwnProperty(i)) {if (catchType(data[i]) === "object") {parent[i] = {}
            stack.push({parent: parent[i],
              data: data[i]
            })
          } else if (catchType(data[i]) === "array") {parent[i] = []
            stack.push({parent: parent[i],
              data: data[i]
            })
          } else {parent[i] = data[i]
          }
        }
      }
    }
    return root
  }

24、公布订阅模式

参考文档
参考文档

25、mvvm

参考文档

26、模仿 map(),filter(),reduce(),some(),flag()

(1)模仿 map

  Array.prototype.mapFn = function (fn, context) {const arr = [].slice.call(this)
    var result = []
    for (var i = 0; i < arr.length; i++) {result.push(fn.call(context, arr[i], i))
    }
    return result
  };
  
  Array.prototype.mapReduce = function (fn, context) {const arr = [].slice.call(this)
    return arr.reduce((prev, cur, index) => {return [...prev, fn.call(context, cur, index)]
    }, [])
  };

(2)模仿 filter

  Array.prototype.filterFn = function (fn, context) {const arr = [].slice.call(this)
    const result = []
    for (var i = 0; i < arr.length; i++) {fn.call(context, arr[i], i) && result.push(arr[i])
    }
    return result
  }

  Array.prototype.filterReduce = function (fn, context) {const arr = [].slice.call(this)
    return arr.reduce((prev, cur, index) => {return fn.call(context, cur, index) ? [...prev, cur] : [...prev]
    }, [])
  };

(3)模仿 some

  Array.prototype.someFn = function (fn, context) {const arr = [].slice.call(this)
    for (var i = 0; i < arr.length; i++) {if (fn.call(context, arr[i], i)) return true
    }
    return false
  }

(4)模仿 reduce

  Array.prototype.reduceFn = function (fn, initial) {const arr = [].slice.call(this)
    let prev
    let startIndex
    if (initial == undefined) {for (let i = 0; i < arr.length; i++) {if (!arr.hasOwnProperty(i)) continue
        startIndex = i
        prev = arr[i]
        break
      }
    } else {prev = initial}
    for (let i = ++startIndex; i < arr.length; i++) {prev = fn.call(null, prev, arr[i], i)
    }
    return prev
  }

(5)模仿 flat

  Array.prototype.flatFn = function (deep) {const arr = [].slice.call(this)
    if (deep == 0) return arr
    return arr.reduce((prev, cur) => {if (Array.isArray(cur)) {return [...prev, ...[].flatFn.call(cur, deep - 1)]
      } else {return [...prev, cur]
      }
    }, [])
  };

27、react 相干

参考文档

正文完
 0