[TOC]

1. 浏览器输入url到页面最后呈现 有哪些过程?

  1. 用户输入URL地址
  2. 浏览器解析URL解析出主机名
  3. 浏览器将主机名转换成服务器ip地址(浏览器先查找本地DNS缓存列表 没有的话 再向浏览器默认的DNS服务器发送查询请求 同时缓存)
  4. 浏览器将端口号从URL中解析出来
  5. 浏览器建立一条与目标Web服务器的TCP连接(三次握手)
  6. 浏览器向服务器发送一条HTTP请求报文
  7. 服务器向浏览器返回一条HTTP响应报文
  8. 关闭连接 浏览器解析文档
  9. 如果文档中有资源 重复6 7 8 动作 直至资源全部加载完毕

2. 浏览器渲染页面流程

  1. 解析HTML生成DOM树。
  2. 解析CSS生成CSSOM规则树。
  3. 将DOM树与CSSOM规则树合并在一起生成渲染树。
  4. 遍历渲染树开始布局,计算每个节点的位置大小信息。
  5. 将渲染树每个节点绘制到屏幕。

https://segmentfault.com/a/1190000010298038

https://juejin.im/post/5a8e242c5188257a6b060000

3. ES6新特性

https://fangyinghang.com/es-6-tutorials/

1、作用域:块级作用域、let、const:

块作用域:

var a = 1;
{
    var a = 3;
}
console.log(a)    // 1

2、箭头函数

  • sum = (a, b) => a + b
  • nums.forEach( v => { console.log(v) })
  • 词法 this

3、参数处理

默认参数值

function multiply(a, b = 1) {
  return a * b;
}

剩余参数:允许我们将一个不定数量的参数表示为一个数组

function sum(...theArgs) {
  return theArgs.reduce((previous, current) => {
    return previous + current;
  });
}
console.log(sum(1, 2, 3));
// expected output: 6

展开运算符:将数组表达式或者string在语法层面展开

function sum(x, y, z) {
  return x + y + z;
}
const numbers = [1, 2, 3];
console.log(sum(...numbers));
// expected output: 6

4、模板字面量

多行字符串

console.log(`string text line 1
string text line 2`);

插入表达式

var a = 5;
var b = 10;
console.log('Fifteen is ' + (a + b) + ' and\nnot ' + (2 * a + b) + '.');
// "Fifteen is 15 and
// not 20."

4. 手写函数防抖和函数节流

防抖和节流的作用都是防止函数多次调用。区别在于,假设一个用户一直触发这个函数,且每次触发函数的间隔小于wait,防抖的情况下只会调用一次,而节流的 情况会每隔一定时间(参数wait)调用函数。

4.1 函数防抖

防抖(用户停止点击后才执行):

// 创建一个防抖函数,它返回一个新的函数,该新函数在指定的 time 时间后执行 fn
function debounce(fn, time = 1000) {
  let timerId = null; // 保存定时器的引用
  return function(...args) {
    const context = this; // 保存当前的 this 上下文
    if (timerId) clearTimeout(timerId);
    timerId = setTimeout(() => {
      fn.apply(context, args);
    }, time);
  };
}
 const debounced = debounce(()=>console.log('hi'))
 debounced()
 debounced()

4.2 函数节流

节流(函数执行一次后,有冷却时间):

function throttle(fn, delay){
     let canUse = true // 设置一个开关,执行一次后,一段时间后再打开
     return function(...args){
       const context = this; // 保存当前的 this 上下文
       if(canUse){
         fn.apply(context, args)
         canUse = false
         setTimeout(()=>{canUse = true}, delay)
       }
     }
 }

 const throttled = throttle(()=>console.log('hi'))
 throttled()
 throttled()

5. 这段代码里的 this 是什么?

  1. fn() this => window/global, 函数里的this就是外面的this
  2. obj.fn() this => obj
  3. fn.call(xx) this => xx
  4. fn.apply(xx) this => xx
  5. fn.bind(xx) this => xx
  6. new Fn() this => 新的对象
  7. fn = ()=> {} this => 外面的 this

关于this的具体讲解:https://zhuanlan.zhihu.com/p/23804247

6. 数组去重

数组的indexOf:返回给定元素在数组中第一次出现的位置的角标,如果没有出现则返回-1

1、利用indexOf去查找新数组是否没有这个值,遍历数组法:

var arr = [2, 2, 3, 4, 5, 3, 5, 7];
for (var i = 0, newArr = [], len = arr.length; i < len; i++) {
  // 在新数组里面查找有没有这个值,没有则添加这个值
  if (newArr.indexOf(arr[i]) === -1) {
    newArr.push(arr[i]);
  }
}
console.log(newArr);

2、数组下标判断法:

var arr = [7, 7, 7, 7, 2, 2, 3, 4, 5, 3, 5, 7];
var newArr = []
for (var i = 0; i < arr.length; i++){
    // 在原数组查找元素,如果返回的角标跟i一样,那么说明这个是第一个值
    if (arr.indexOf(arr[i]) === i) {
        newArr.push(arr[i]);
    }
}
console.log(newArr);

3、ES6简化版:

var arr = [2, 2, 3, 4, 5, 3, 5, 7, 7, 7, 7, 8, 8];
var newArr = [...new Set(arr)]; 
console.log(newArr);

var newArr = Array.from(new Set(arr));
console.log(newArr);

4、利用对象的属性不会重复这一特性,首先创建一个空对象,然后用 for 循环遍历,来校验数组元素是否重复。

var arr = [7, 7, 7, 7, 2, 2, 3, 4, 5, 3, 5, 7];
var newArr = []
var obj = {}
for (var i = 0; i < arr.length; i++) {
    if (!obj[arr[i]]) { // 或者:obj[arr[i]] === undefined
        newArr.push(arr[i])
        obj[arr[i]] = 1
    }
}
console.log(obj);
console.log(newArr);

数组是对象,根据对象某个属性去重

var arr = [{name: 'jack'}, {name: 'jack'}, {name: 'tom'}, {name: 'tom'}]
var newArr = []
arr.forEach((item) => {
  if (newArr.every(e => e.name !== item.name)) {
    newArr.push(item)
  }
})
console.log('newArr: ', newArr)

7. 实现继承

原型:每个构造函数都有一个prototype属性,这个属性指向一个对象,这个对象称为原型对象。

原型链:由于原型对象也是对象,所以原型对象也有自己的原型对象,因此形成了原型链,而所有对象原型链最终都会指向Object的原型。

原型对象的constuctor属性:默认指向prototype对象所在的构造函数。

constuctor作用:1、可以知道实例对象是哪一个构造函数创建的。2、可以通过实例对象的constructor属性新建另一个实例对象。

ES5继承分成两步实现:

第一步、是在子类的构造函数中,调用父类的构造函数。目的是让子类实例继承父类实例的属性和方法。

第二步、是让子类的原型指向父类的原型,目的是让子类继承父类原型上的属性和方法。

注意:不能让子类的原型直接等于父类原型,如果这样做那么子类原型和父类原型是同一个对象,那么子类在原型上添加方法,父类原型也修改了,导致父类对象也能获取到这个方法。

function Animal(color){
     this.color = color
 }
 Animal.prototype.move = function(){}
// 第一步:子类构造函数调用父类构造函数
 function Dog(color, name){
     Animal.call(this, color) // 或者 Animal.apply(this, arguments)
     this.name = name
 }
 // 第二步:子类的原型指向父类的原型。Dog.prototype = Animal.prototype
 function f(){}
 f.prototye = Animal.prototype
 Dog.prototype = new f()
 Dog.prototype.constuctor = Dog
 Dog.prototype.say = function(){ console.log('汪')}

 var dog = new Dog('黄色','阿黄');

ES6实现:

class Animal{
     constructor(color){
         this.color = color
     }
     move(){}
 }
 class Dog extends Animal{
     constructor(color, name){
         super(color)
         this.name = name
     }
     say(){}
 }

8. typeof和instanceof区别

关于typeof

typeof一元运算符,用来获取一个变量或者表达式的类型,typeof一般只能返回如下几个结果:

number,boolean,string,function(函数),object(NULL,数组,对象),undefined。

 var a = [34,4,3,54],
     b = 34,
     c = 'adsfas',
     d = function(){console.log('我是函数')},
     e = true,
     f = null,
     g;

console.log(typeof(a));//object
console.log(typeof(b));//number
console.log(typeof(c));//string
console.log(typeof(d));//function
console.log(typeof(e));//boolean
console.log(typeof(f));//object
console.log(typeof(g));//undefined

关于instanceof

语法:object instanceof constructor

说明:instanceof 运算符用来检测 constructor.prototype是否存在于参数 object 的原型链上

参数说明:

object:某个实例对象,如果不是对象则返回false

constructor:某个构造函数,如果不是函数则抛出typeError

https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Operators/instanceof

instance中文翻译为实例,因此instanceof判断该对象是谁的实例,同时我们也就知道instanceof是对象运算符。

这里的实例就牵扯到了对象的继承,它的判断就是根据原型链进行搜寻,在对象obj1的原型链上如果存在另一个对象obj2的原型属性,那么表达式(obj1 instanceof obj2)返回值为true;否则返回false。

var a = new Array();
var b = new Object();
var c = new RegExp();
var d = function(){};
var e = new d();
var f = new String();

console.log(a instanceof Array); // true
console.log(a instanceof Object); // true
console.log(b instanceof Object); // true
console.log(c instanceof RegExp); // true
console.log(c instanceof Object); // true
console.log(d instanceof Function); // true
console.log(d instanceof Object); // true
console.log(e instanceof d); // true
console.log(f instanceof String); // true
// 左边不是对象,返回false
console.log('12313' instanceof String) // false
// 右边不是构造函数,报错
console.log(f instanceof f)
// Uncaught TypeError: Right-hand side of 'instanceof' is not an object

两者的区别:

  • typeof判断所有变量的类型,返回值有number,boolean,string,function,object,undefined。

  • typeof对于丰富的对象实例,只能返回"object"字符串。

  • instanceof用来判断对象,代码形式为obj1 instanceof obj2(obj1是否是obj2的实例),obj2必须为构造函数,否则会报错!其返回值为布尔值。

  • instanceof可以对不同的对象实例进行判断,判断方法是根据对象的原型链依次向下查询,如果obj2的原型属性存在obj1的原型链上,(obj1 instanceof obj2)值为true。

9. 对象的深拷贝方法实现

function deepClone(obj) {
    var newObj = {}
    for (var key in obj) {
        // 如果是自身属性
        if (Object.hasOwnProperty.call(obj, key)) {
            if (Array.isArray(obj[key])) {
                // 判断是数组,需要单独处理
                newObj[key] = obj[key].concat()
            }
            else if (typeof obj[key] === 'object') {
                // 属性是对象,提柜调用
                newObj[key] = deepClone(obj[key])
            } 
            else {
                // 原始值和函数直接复制
                newObj[key] = obj[key]
            }
        }
    }
    return newObj
}

var obj = {
    func: function() { console.log('hello') },
    arr: [1, 2, 3, 4, 5],
    subObj: { name: 'jack', },
    age: 12,
}
var newObj = deppClone(obj)
console.log('newObj: ', newObj)

10. 手动实现一个new方法

在《JavaScript模式》这本书中,new的过程说的比较直白,当我们new一个构造器,主要有三步:

1、创建一个空对象,将它的引用赋给 this,继承函数的原型。
2、通过 this 将属性和方法添加至这个对象
3、最后返回 this 指向的新对象,也就是实例(如果没有手动返回其他的对象)

所以实现一个new分以下几步:

1、创建一个空对象。

2、将空对象的原型设置成构造函数的原型。

3、使用call方法执行构造函数绑定空对象,初始化这个对象。

4、返回这个新建对象

function createNewObj(func) {
    // 创建一个空的对象
    var obj = {}
    // 设置空对象的原型,指向构造函数的原型链
    Object.SetPrototypeOf(obj, func.prototype)
    // 或者: obj.__proto__ = func.prototype

    // 执行构造函数使用call方法绑定空对象,初始化这个对象。
    func.call(obj)
    // 返回这个新建对象
    return obj
}

11. 箭头函数和普通函数区别,箭头函数可以当构造函数用吗?

区别:

1、写法不同,箭头函数使用箭头定义,写法简洁。 普通函数使用function定义。

2、箭头函数都是匿名函数,而普通函数既可以是匿名函数,也可以是具名函数。

3、this指向不同,箭头函数没有this,在声明的时候,捕获上下文的this供自己使用,一旦确定不会再变化。在普通函数中,this指向调用自己的对象,如果用在构造函数,this指向创建的对象实例。普通函数可以使用call,apply,bind改变this的指向。

4、箭头函数没有arguments(实参列表,类数组对象),每一个普通函数在调用后都有一个arguments对象,用来存储实际传递的参数。

5、箭头函数不能作为构造函数来使用,普通函数可以用作构造函数,一次来创建一个对象的实例。

6、箭头函数没有原型,而普通函数有。

箭头函数不能当构造函数使用new原因:

1、箭头函数没有原型prototype,new方法需要函数有原型指向创建的对象。

2、箭头函数没有this,new方法需要函数内部this指向新建的对象,然后设置新对象

更多内容:

https://blog.csdn.net/weixin_43288600/article/details/135197555

powered by Gitbook文件最后修改时间: 2024-08-15 12:00:18

results matching ""

    No results matching ""