[TOC]
1. 浏览器输入url到页面最后呈现 有哪些过程?
- 用户输入URL地址
- 浏览器解析URL解析出主机名
- 浏览器将主机名转换成服务器ip地址(浏览器先查找本地DNS缓存列表 没有的话 再向浏览器默认的DNS服务器发送查询请求 同时缓存)
- 浏览器将端口号从URL中解析出来
- 浏览器建立一条与目标Web服务器的TCP连接(三次握手)
- 浏览器向服务器发送一条HTTP请求报文
- 服务器向浏览器返回一条HTTP响应报文
- 关闭连接 浏览器解析文档
- 如果文档中有资源 重复6 7 8 动作 直至资源全部加载完毕
2. 浏览器渲染页面流程
- 解析HTML生成DOM树。
- 解析CSS生成CSSOM规则树。
- 将DOM树与CSSOM规则树合并在一起生成渲染树。
- 遍历渲染树开始布局,计算每个节点的位置大小信息。
- 将渲染树每个节点绘制到屏幕。
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 是什么?
- fn() this => window/global, 函数里的this就是外面的this
- obj.fn() this => obj
- fn.call(xx) this => xx
- fn.apply(xx) this => xx
- fn.bind(xx) this => xx
- new Fn() this => 新的对象
- 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