1、几种垂直居中方式
vertical-align:middle
.parent{display:table-cell;vertical-align:middle;text-align: center;}
.child{display: inline-block;}
display:flex
.parent{display: flex;justify-content: center;align-items: center;}
.child{}
transform
.child{position:relative;top:50%;left:50%;transform:translate(-50%,-50%);}
position
.parent{position: relative;}
.child{position: absolute;left:50%;margin-left:-100px;}
2、闭包
闭包是指有权访问另一个函数作用域中的变量的函数
var dom=document.getElementsByTagName("li");
for(var i=0;i<items.length;i++){
items[i].onclick=(function(num){
return function(){
console.log(num);
}
})(i);
}
3、async函数返回一个 Promise 对象,可以使用then方法添加回调函数。当执行到await的时候会先执行await,等完成后再继续。
4、双向绑定实现原理
通过Object.defineProperty的方法定义 get和set方法对数据的操作拥有代理权,并且每个属性都会有一个对应的依赖
5、v-model实现原理
v-model 本质是一个语法糖监听input事件每次输入时候监听data值变化之后把值绑定到value上
6、webpack打包原理
webpack只是一个打包模块的机制,只是把依赖的模块转化成可以代表这些包的静态文件。webpack做的就是分析代码。转换代码,编译代码,输出代码。webpack本身是一个node的模块
7、loader原理
loader 本质上是一个函数,输入参数是一个字符串,输出参数也是一个字符串。输出的参数会被当成是 JS 代码,从而被 esprima 解析成 AST,触发进一步的依赖解析。webpack会按照从右到左的顺序执行loader。
8、函数防抖和节流
防抖(debounce) 就是指触发事件后在 n 秒内函数只能执行一次,如果在 n 秒内又触发了事件,则会重新计算函数执行时间。
/**
* @desc 函数防抖
* @param func 函数
* @param wait 延迟执行毫秒数
* @param immediate true 表立即执行,false 表非立即执行
*/
function debounce(func,wait,immediate) {
let timeout;
return function () {
let context = this;
let args = arguments;
if (timeout) clearTimeout(timeout);
if (immediate) {
var callNow = !timeout;
timeout = setTimeout(() => {
timeout = null;
}, wait)
if (callNow) func.apply(context, args)
}
else {
timeout = setTimeout(function(){
func.apply(context, args)
}, wait);
}
}
}
节流(throttle) 所谓节流,就是指连续触发事件但是在 n 秒中只执行一次函数。节流会稀释函数的执行频率。
/**
* @desc 函数节流
* @param func 函数
* @param wait 延迟执行毫秒数
* @param type 1 表时间戳版,2 表定时器版
*/
function throttle(func, wait ,type) {
if(type===1){
let previous = 0;
}else if(type===2){
let timeout;
}
return function() {
let context = this;
let args = arguments;
if(type===1){
let now = Date.now();
if (now - previous > wait) {
func.apply(context, args);
previous = now;
}
}else if(type===2){
if (!timeout) {
timeout = setTimeout(() => {
timeout = null;
func.apply(context, args)
}, wait)
}
}
}
}
9、JavaScript 事件代理和委托
为父节点添加一个click事件,当子节点被点击的时候,click事件会从子节点开始向上冒泡。父节点捕获到事件之后,通过判断e.target.nodeName来判断是否为我们需要处理的节点。并且通过e.target拿到了被点击的Li节点。从而可以获取到相应的信息,并作处理。
10、理解js继承的6种方式
- 原型链继承
让新实例的原型等于父类的实例 - 构造函数继承
用.call()和.apply()将父类构造函数引入子类函数(在子类函数中做了父类函数的自执行(复制)) - 组合继承(组合原型链继承和借用构造函数继承)
结合了两种模式的优点,传参和复用 - 原型式继承
用一个函数包装一个对象,然后返回这个函数的调用,这个函数就变成了个可以随意增添属性的实例或对象。object.create()就是这个原理。 - 寄生式继承
就是给原型式继承外面套了个壳子。 - 寄生组合式继承(常用)
在函数内返回对象然后调用
11、call、apply、bind
可以改变this指向。
call用到构造函数复制父类实例给子类
// 父类
function SuperType (name) {
this.name = name; // 父类属性
}
SuperType.prototype.sayName = function () { // 父类原型方法
return this.name;
};
// 子类
function SubType () {
// 调用 SuperType 构造函数
// 在子类构造函数中,向父类构造函数传参
SuperType.call(this, 'SuperType');
// 为了保证子父类的构造函数不会重写子类的属性,需要在调用父类构造函数后,定义子类的属性
this.subName = "SubType";
// 子类属性
};
// 子类实例
let instance = new SubType();
// 运行子类构造函数,并在子类构造函数中运行父类构造函数,this绑定到子类
解决var作用域问题
var bottle = [
{name: 'an', age: '24'},
{name: 'anGe', age: '12'}
];
for (var i = 0; i < bottle.length; i++) {
// 匿名函数
(function (i) {
setTimeout(() => {
// this 指向了 bottle[i]
console.log('#' + i + ' ' + this.name + ': ' + this.age);
}, 1000)
}).call(bottle[i], i);
// 调用 call 方法,同时解决了 var 作用域问题
}
apply() 方法调用一个具有给定this值的函数,以及作为一个数组(或[类似数组对象)提供的参数。
- call、apply 与 扩展运算符
let args = [1, 2, 3];
func.call(context, ...args); // 使用 spread 运算符将数组作为参数列表传递
func.apply(context, args); // 与使用 call 相同
- 扩展运算符 … 允许将 可迭代的 参数列表 作为列表传递给 call。
- apply 只接受 类数组一样的 参数列表。
- apply 函数转移
apply 最重要的用途之一是将调用传递给另一个函数,如下所示:
let wrapper = function() {
return anotherFunction.apply(this, arguments);
};
- apply 连接数组
array.push.apply 将数组添加到另一数组上:
var array = ['a', 'b']
var elements = [0, 1, 2]
array.push.apply(array, elements)
console.info(array) // ["a", "b", 0, 1, 2]
- apply 来链接构造器
Function.prototype.constructor = function (aArgs) {
var oNew = Object.create(this.prototype);
this.apply(oNew, aArgs);
return oNew;
};
- apply 和内置函数
/* 找出数组中最大/小的数字 */
let numbers = [5, 6, 2, 3, 7]
/* 应用(apply) Math.min/Math.max 内置函数完成 */
let max = Math.max.apply(null, numbers)
/* 基本等同于 Math.max(numbers[0], ...) 或 Math.max(5, 6, ..) */
let min = Math.min.apply(null, numbers)
console.log('max: ', max)
// max: 7
console.log('min: ', min)
// min: 2
Function.prototype.bind()
经常犯的一个错误是将一个方法从对象中拿出来,然后再调用,希望方法中的 this 是原来的对象(比如在回调中传入这个方法)。如果不做特殊处理的话,一般 this 就丢失了。
例如:
let bottle = {
nickname: "bottle",
sayHello() {
console.log(`Hello, ${this.nickname}!`)
},
sayHi(){
setTimeout(function(){
console.log('Hello, ', this.nickname)
}, 1000)
}
};
// 问题一
bottle.sayHi();
// Hello, undefined!
// 问题二
setTimeout(bottle.sayHello, 1000);
// Hello, undefined!
问题一的 this.nickname 是 undefined ,原因是 this 指向是在运行函数时确定的,而不是定义函数时候确定的,再因为 sayHi 中 setTimeout 在全局环境下执行,所以 this 指向 setTimeout 的上下文:window。
问题二的 this.nickname 是 undefined ,是因为 setTimeout 仅仅只是获取函数 bottle.sayHello 作为 setTimeout 回调函数,this 和 bottle 对象分离了。
bind() 最简单的用法是创建一个新绑定函数,当这个新绑定函数被调用时,this 键值为其提供的值,其参数列表前几项值为创建时指定的参数序列,绑定函数与被调函数具有相同的函数体(ES5中)。 问题二可以写为:
let bottle = {
nickname: "bottle",
sayHello() {
console.log(`Hello, ${this.nickname}!`);
},
sayHi(){
// 使用 bind
setTimeout(function(){
console.log('Hello, ', this.nickname)
}.bind(this), 1000)
// 或箭头函数
setTimeout(() => {
console.log('Hello, ', this.nickname)
}, 1000)
}
};
// 问题一:完美解决
bottle.sayHi()
// Hello, bottle
// Hello, bottle
let sayHello = bottle.sayHello.bind(bottle); // (*)
sayHello();
// Hello, bottle!
// 问题二:完美解决
setTimeout(sayHello, 1000);
// Hello, bottle!
// 更新 bottle
bottle = {
nickname: "haha",
sayHello() {
console.log(`Hi, ${this.nickname}!`)
}
};
11、构造函数、原型、原型链
- 构造函数
构造函数 ,是一种特殊的方法。主要用来在创建对象时初始化对象。每个构造函数都有prototype(原型)属性 - 原型
每个函数都有prototype(原型)属性,这个属性是一个指针,指向一个对象,这个对象的用途是包含特定类型的所有实例共享的属性和方法,即这个原型对象是用来给实例共享属性和方法的。 而每个实例内部都有一个指向原型对象的指针。 - 原型链
每个构造函数都有一个原型对象,原型对象都包含一个指向构造函数的指针,而实例都包含指向原型对象内部的指针。我们让原型对象的实例(1)等于另一个原型对象(2), 此时原型对象(2)将包含一个指向原型对象(1)的指针, 再让原型对象(2)的实例等于原型对象(3),如此层层递进就构成了实例和原型的链条,这就是原型链的概念 - 要理解原型和原型链首先要知道几个概念:
(1) 在js里,继承机制是原型继承。继承的起点是 对象的原型(Object prototype)。
(2) 一切皆为对象,只要是对象,就会有 proto 属性,该属性存储了指向其构造的指针。 Object prototype也是对象,其 proto 指向null。
(3) 对象分为两种:函数对象和普通对象,只有函数对象拥有『原型』对象(prototype)。
- prototype的本质是普通对象。
- Function prototype比较特殊,是没有prototype的函数对象。
- new操作得到的对象是普通对象。
(4) 当调取一个对象的属性时,会先在本身查找,若无,就根据 proto 找到构造原型,若无,继续往上找。最后会到达顶层Object prototype,它的 proto 指向null,均无结果则返回undefined,结束。
(5) 由 proto 串起的路径就是『原型链』。