每天进步一点点:4.7

本文介绍了JavaScript中数组的方法如push、reduce等,详细解读了事件流机制,包括捕获、目标和冒泡阶段,以及事件监听和委托的区别。还探讨了宏任务与微任务的概念,以及如何手写斐波那契数列和希尔排序。此外,深入讲解了不同类型的继承方式及其优缺点,涉及原型继承、call继承和类继承。
摘要由CSDN通过智能技术生成

大家好,我是梅巴哥er。本篇继续努力,总结知识点。


1,问:数组有哪些方法

答:

增加元素的: push, unshift, splice
删除元素的:pop, shift
查元素的:find, indexOf, includes, some, filter
判断数组的:Array.isArray()
转化成数组的:Array.from, Array.of, split
排序的:sort
拉平数组的:flat
遍历数组的:map, forEach, for
数组反转的:reverse
数组累加的:reduce
2,手写代码:用reduce实现map
Array.prototype.myMap = function(currentValue,thisArg){
  let arr = []
  thisArg = thisArg || []
  this.reduce((pre,curr,index) => {
    arr.push(currentValue.call(thisArg,curr,index))
  },[])
  return arr;
}
// 举例
var arr = [1, 2, 3]
arr.myMap(v => console.log(v))// 1 2 3
3,问:什么是事件流?添加事件监听的方法有哪些?什么是事件委托(代理)?

答:

  • 事件流
    • 当html元素产生一个事件时,这个事件会沿着元素节点和根节点之间的路径传播,路径所经过的节点都会收到该事件。这个传播过程,就叫事件流。
  • 分为3个阶段
    • 捕获阶段
    • 目标阶段
    • 冒泡阶段
  • 添加事件监听的方法
    • DOM0:节点.on事件名 = function(){}
    • DOM2:节点.addEventListener(‘事件名’, function(){})
  • 事件委托(也叫事件代理、事件委派)
    • 把子节点的事件,绑定在父节点上,当子节点触发事件时,父节点可以通过冒泡原理接收到该事件,然后对事件进行监听和处理。
4,问:什么是宏任务、微任务?

答:

  • JS是单线程的,当任务挂起时,任务会分为两类,即同步任务和异步任务。
  • 执行时,会先执行主线程的同步任务,然后会读取放在任务队列里的异步任务。有要执行的异步任务,就放进主线程里执行。然后循环这个步骤(这就叫事件循环)
  • 在读取和执行任务队列里的异步任务时,又把异步任务分为宏任务和微任务。按照各自的顺序来读取和执行。
  • 执行方法是:先执行一个宏任务,执行完后,会检查有没有微任务要执行。有,则执行完微任务,再去执行下一个宏任务。没有,则跳到下一个宏任务,继续执行。
  • 宏任务有哪些:
    • 同步代码, I/O,setTimeout,setInterval, setImmediate, requestAnimationFrame
  • 微任务有哪些:
    • process.nextTick, Promise.then catch finally
  • 宏任务优先级:
    • 主代码块 > setImmediate > MessageChannel > setTimeout / setInterval
  • 微任务优先级:
    • process.nextTick > Promise = MutationObserver
// 看下该代码块的输出结果
//主线程直接执行
console.log('1');
//丢到宏事件队列中
setTimeout(function() {
    console.log('2');
    process.nextTick(function() {
        console.log('3');
    })
    new Promise(function(resolve) {
        console.log('4');
        resolve();
    }).then(function() {
        console.log('5')
    })
})
//微事件1
process.nextTick(function() {
    console.log('6');
})
//主线程直接执行
new Promise(function(resolve) {
    console.log('7');
    resolve();
}).then(function() {
    //微事件2
    console.log('8')
})
//丢到宏事件队列中
setTimeout(function() {
    console.log('9');
    process.nextTick(function() {
        console.log('10');
    })
    new Promise(function(resolve) {
        console.log('11');
        resolve();
    }).then(function() {
        console.log('12')
    })
})

5,手撕代码:写个斐波那契数列
function fn(a) {
  if(a === 1) {
    return 0
  } else if(a === 2) {
    return 1
  } else {
    return fn(a-2) + fn(a-1)
  }
}

for(var i = 1; i < 10; i++) {
  console.log(fn(i))
}
// 0 1 1 2 3 5 8 13 21
6,手撕代码:写个希尔排序
7,手撕代码:js类的继承方式,说明区别和优缺点

答:

  • 原型继承
  • call继承(基本继承)
  • 组合继承(原型+call)
  • 寄生继承
  • 寄生组合继承
  • class类继承

// 1,原型继承
function Star(age) {
  this.name = 'dilireba'
  this.age = age
  this.art = ['sing', 'dance']
}
Star.prototype.getName = function () {
  console.log(this.name)
}
function Dili(age) {
  this.age = age
}
Dili.prototype = new Star('18') //区分1:Star调用了几次
var dili1 = new Dili('20') // 区分2:子类实例能不能给父类传参数
var dili2 = new Dili('22')
dili1.getName() // 区分3:子类实例是否能调用父类原型上的属性和方法
console.log(dili1.age, dili2.age)
dili1.art.push('show') // 区分4:子类实例调用父类引用数据类型时,数据是否共享
console.log(dili1.art)
console.log(dili2.art)
console.log(dili1 instanceof Star) // 区分5:子类实例是否为父类的实例

// 2,call继承
function Star(age) {
  this.name = 'dilireba'
  this.age = age
  this.art = ['sing', 'dance']
}
Star.prototype.getName = function () {
  console.log(this.name)
}
function Dili(age) {
  Star.call(this, age)
  this.age = age
}
// Dili.prototype = new Star('18') //区分1:Star调用了几次
var dili1 = new Dili('20') // 区分2:子类实例能不能给父类传参数
var dili2 = new Dili('22')
// dili1.getName() // 区分3:子类实例是否能调用父类原型上的属性和方法
console.log(dili1.age, dili2.age)
dili1.art.push('show') // 区分4:子类实例调用父类引用数据类型时,数据是否共享
console.log(dili1.art)
console.log(dili2.art)
console.log(dili1 instanceof Star) // 区分5:子类实例是否为父类的实例

// 3,原型+call组合继承
function Star(age) {
  this.name = 'dilireba'
  this.age = age
  this.art = ['sing', 'dance']
}
Star.prototype.getName = function () {
  console.log(this.name)
}
function Dili(age) {
  Star.call(this, age)
  this.age = age
}
Dili.prototype = new Star('18') //区分1:Star调用了几次
var dili1 = new Dili('20') // 区分2:子类实例能不能给父类传参数
var dili2 = new Dili('22')
dili1.getName() // 区分3:子类实例是否能调用父类原型上的属性和方法
console.log(dili1.age, dili2.age)
dili1.art.push('show') // 区分4:子类实例调用父类引用数据类型时,数据是否共享
console.log(dili1.art)
console.log(dili2.art)
console.log(dili1 instanceof Star) // 区分5:子类实例是否为父类的实例

// 4,寄生继承(对象拷贝继承)
// 就是拷贝一份父对象,继承父对象的属性和方法,
// 然后往生成的新对象里,可以添加属性和方法,最终return新拷贝的对象
let Star = {
  name: 'dilireba',
  art: ['sing', 'dance'],
  getName: function() {
    console.log(this.name)
  },
}

function Dili(par) {
  let copy = Object.create(par)
  // console.log(copy)
  copy.getArt = function() {
    console.log(this.art)
  }
  return copy
}

let dili1 = Dili(Star)
let dili2 = Dili(Star)
console.log(dili1.name, dili2.name)
dili1.art.push('show')
console.log(dili1.art, dili2.art)
dili1.getArt()
dili1.getName()
// 优点是可以添加更多属性和方法
// 缺点和原型一样,而且这个没有实例化,没有用到原型

// 5,寄生组合继承(原型拷贝 + call)
// 目前的最优解
function Star(age) {
  this.name = 'dilireba'
  this.age = age
  this.art = ['sing', 'dance']
}
Star.prototype.getName = function() {
  console.log(this.name)
}
function Dili(age) {
  Star.call(this, age)
  this.age = age
}
Dili.prototype = Object.create(Star.prototype)
// 注意这里要修改指向,
// 因为在JS里,原型的构造函数是要指向他自己的
// 如果不修改,就会指向父类了,不符合JS规则
Dili.prototype.constructor = Dili

var dili1 = new Dili('20')
var dili2 = new Dili('22')
dili1.getName()
console.log(dili1.age, dili2.age)
dili1.art.push('show')
console.log(dili1.art, dili2.art)
console.log(dili1 instanceof Star)

// 6,class类的extens继承
// 该方法经过Babel编译后,也是用的寄生组合继承方式

8,问:说说JS事件循环(Event Loop)

答:

  • JS是单线程的脚本语言
  • 主要用途是与用户实现交互和操作DOM
  • 执行任务时,所有的任务被分为两种:同步任务和异步任务
  • 同步任务:就是在主线程上排队执行的任务,前一个任务执行完毕,才能执行下一个任务
  • 异步任务:就是不进入主线程,而是进入任务队列的任务。只有任务队列通知主线程,某个异步任务可以执行了,该任务才会进入主线程里执行。
  • 机制如下:
    • 如果是同步任务,就在主线程中执行,形成一个执行栈
    • 主线程之外,还有一个任务队列,用来放异步任务
    • 当同步任务执行完后,就会检查任务队列。看下任务队列里有哪些异步任务要执行,如果有,就读取该任务,放进主线程里执行。
    • 重复上面的第三个步骤,不断检查、读取和执行。
9,手撕代码:异步发起1000次请求,多线程进行,怎么写?
10,问:map和set区别

答:

  • Set 和 Map 主要的应用场景在于 数据重组 和 数据储存,都可以遍历,都有增删查清的方法。
    • set里面都是值,不重复且无序,有add, delete, has ,clear等方法
    • map里面是键值对形式,有set, get, has, delet, clear等方法
11,问:js基本数据类型

答: string, number, boolean, null, undefined, symbol

12,问:Object.defineProperty()

答:

  • Object.defineProperty()用于给对象增加或修改属性。
  • 语法:
Object.defineProperty(obj, attr, {
	value: , // 该属性对应的值
	writable: , // 值是否能改变
	enumerable: , // 该属性是否允许被遍历
	configurable: , // 以上的描述符是否能被修改,值是否能删除
	get() {...}, // 获取属性时触发的函数
	set() {...}, // 设置属性时触发的函数
})
13,问:js中变量提升是什么,有哪些危害,以及const let和var区别

答:

  • 在ES5中,变量的声明会被提升到当前作用域的顶部。变量可以先使用,后声明。
// 例1
console.log(x) // x未声明,会报错

// 例2
console.log(x) // 会输出undefined,不会报错
var x // 变量声明被提升到顶部

// 例3
x = 1
console.log(x) // 输出1 .变量可以先使用,后声明
var x

// 例4
console.log(fn()) // 输出1 .声明式函数的提升是整个函数体被提升

function fn() {
  return 1
}

// 例5
fn2() // 报错,只提升了变量,没提升函数体。执行时不知道fn2是函数
var fn2 = function () {
  console.log(2)
}

// 来道题吧
var a = 0;
if (true) {
    a = 1;
    function a() {};
    a = 21;
    console.log(a)
}
console.log(a);
// 21 1 
  • var 声明的变量,会有变量提升,可以先使用后声明,可以重复声明,声明的值可以改变,有全局变量和局部变量,没有块级作用域
  • let 没有变量提升,必须先声明后使用,不能用let重复声明,声明的值可以更改,会形成块级作用域
  • const 和let基本一致,但是声明后的值不能改变,声明的数组和对象里的值是能改变的
14,手撕代码:css垂直居中

答:

// 不知道父盒子宽高的情况下,说几种方法吧
// flex
父盒子: display: flex;
	align-items: center;
// margin + transform
子盒子:margin: 50% auto;
       transform: translateY(-50%);
// postion + transform
父盒子:position: relative;
子盒子:position: absolute;
		top: 50%;
		left: 50%;
		transform: translate(-50%, -50%);
15,虚拟DOM

答:

  • 虚拟DOM是什么
    • 是用JS模拟DOM树的一种树形结构,它包含了整个DOM树的结构信息
  • 虚拟DOM是用来做什么的
    • 减少对真实DOM的操作,部分DOM的变化不再需要重新渲染整个页面,从而优化性能
  • 虚拟DOM的原理是什么
    • 1,用JS对象结构来表示真实DOM树结构,
    • 2,在状态变更时,重新构造一棵新的DOM树。用新树对象结构和老树的对象结构做比较,记录两棵树的差异
    • 3,把差异更新到老树上,老树对象结构插入页面,更新视图
16,问:前端网页渲染流程是怎样的

答:
浏览器拿到html文件后,开始渲染:

  • 根据html文件,构建DOM树和CSSOM树。构建DOM树期间,如果遇到JS代码,阻塞DOM树和CSSOM树的构建,优先加载JS文件。JS文件加载完毕后,再继续构建DOM树和CSSOM树。
  • 构建渲染树(Render Tree)
  • 页面的重排重绘。页面渲染完成后,若JS操作了DOM节点,浏览器对页面进行重排或重绘。

以上。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值