实现一个字符串匹配算法,从长度为 n 的字符串 S中,查找是否存在字符串 T,T 的长度是 m,若存在返回所在位置
function find(S,T){
if(!S || !T || S.length < T.length) return -1 //如果一方为空或者S长度小于T长度
for(var i = 0 ;i < S.length; i++){
if(S.slice(i,i+T.length) == T) return i
}
return -1
}
为什么普通 for 循环的性能远远高于 forEach 的性能,请解释其中的原因。
for 循环没有任何额外的函数调用栈和上下文;forEach 函数签名实际上是array.forEach(function(currentValue, index, arr), thisValue)
它不是普通的 for 循环的语法糖,还有诸多参数和上下文需要在执行的时候考虑进来,
这里可能拖慢性能;
介绍下 BFC、IFC、GFC 和 FFC
BFC(Block formatting contexts):
块级格式上下文页面上的一个隔离的渲染区域,那么他是如何产生的呢?可以触发 BFC 的元素有 float、position、overflow、display:table-cell/inline-block/table-caption/flex/inline-flex ;BFC 有什么作用呢?清除浮动,防止margin塌陷等。
IFC(Inline formatting contexts):
内联格式上下文 IFC 的 line box(线框)高度由其包含行内元素中最高的实际高
度计算而来(不受到竖直方向的 padding/margin 影响)IFC 中的 line box 一般左右
都贴紧整个 IFC,但是会因为 float 元素而扰乱。float 元素会位于 IFC与 line box
之间,使得 line box 宽度缩短。 同个 ifc 下的多个 line box 高度会不同. IFC 中时
不可能有块级元素的,当插入块级元素时(如 p 中插入 div)会产生两个匿名块
与 div 分隔开,即产生两个 IFC,每个 IFC 对外表现为块级元素,与 div 垂直排
列。那么 IFC 一般有什么用呢?水平居中:当一个块要在环境中水平居中时,
设置其为 inline-block 则会在外层产生 IFC,通过 text-align 则可以使其水平居中。
垂直居中:创建一个 IFC,用其中一个元素撑开父元素的高度,然后设置其vertical-align:middle,其他行内元素则可以在此父元素下垂直居中。
GFC(GrideLayout formatting contexts):
网格布局格式化上下文当为一个元素设置 display 值为 grid 的时候,此元素将会
获得一个独立的渲染区域,我们可以通过在网格容器(grid container)上定义
网格定义行(grid definition rows)和网格定义列(grid definition columns)属性
各在网格项目(grid item)上定义网格行(grid row)和网格列(grid columns)
为每一个网格项目(grid item)定义位置和空间。那么 GFC 有什么用呢,和 table
又有什么区别呢?首先同样是一个二维的表格,但 GridLayout 会有更加丰富的
属性来控制行列,控制对齐以及更为精细的渲染语义和控制。
FFC(Flex formatting contexts):
自适应格式上下文 display 值为 flex 或者 inline-flex 的元素将会生成自适应容器
(flex container),可惜这个牛逼的属性只有谷歌和火狐支持,不过在移动端
也足够了,至少 safari 和 chrome 还是 OK 的,毕竟这俩在移动端才是王道。Flex
Box 由伸缩容器和伸缩项目组成。通过设置元素的 display 属性为 flex 或
inline-flex 可以得到一个伸缩容器。设置为 flex 的容器被渲染为一个块级元素,
而设置为 inline-flex 的容器则渲染为一个行内元素。伸缩容器中的每一个子元
素都是一个伸缩项目。伸缩项目可以是任意数量的。伸缩容器外和伸缩项目内
的一切元素都不受影响。简单地说,Flexbox 定义了伸缩容器内伸缩项目该如
何布局。
使用 JavaScript Proxy 实现简单的数据绑定
const model = document.getElementById("model")
const word = document.getElementById("word")
var obj= {}; //obj可以是对象,可以是数组,也可以是Proxy,生成代理对象也是对应的
const newObj = new Proxy(obj, { //拦截对象
get: function(target, key, receiver) {
return Reflect.get(target, key, receiver); //等同于return target[key]
},
set: function(target, key, value, receiver) {
if (key === "text") { //拦截属性
model.value = value; //更新dom
word.innerHTML = value; //更新dom
}
return Reflect.set(target, key, value, receiver); //等同于target[key] = value
}
});
model.addEventListener("keyup",function(e){
console.log(newObj.text) //触发getter
newObj.text = e.target.value //触发setter
})
数组里面有 10 万个数据,取第一个元素和第 10 万个元素的时间相差多少
数组可以直接根据索引取的对应的元素,所以不管取哪个位置的元素的时间复
杂度都是 O(1)得出结论:消耗时间几乎一致,差异可以忽略不计
输出以下代码运行结果
// example 1
var a={}, b='123', c=123;
a[b]='b';
a[c]='c';
console.log(a[b]);
---------------------
// example 2
var a={}, b=Symbol('123'), c=Symbol('123');
a[b]='b';
a[c]='c';
console.log(a[b]);
---------------------
// example 3 var a={}, b={key:'123'}, c={key:'456'};
a[b]='b';
a[c]='c';
console.log(a[b]);
1.对象的键名只能是字符串和 Symbol 类型。
2.其他类型的键名会被转换成字符串类型。
3.对象转字符串默认会调用 toString 方法。
// example 1
var a={}, b='123', c=123;a[b]='b';
// c 的键名会被转换成字符串'123',这里会把 b 覆盖掉。a[c]='c';
// 输出 c
// example 2
var a={}, b=Symbol('123'), c=Symbol('123');
// b 是 Symbol 类型,不需要转换。a[b]='b';
// c 是 Symbol 类型,不需要转换。任何一个 Symbol 类型的值都是不相等的, 所以不会覆盖掉 b。a[c]='c';
// 输出 b
// example 3
var a={}, b={key:'123'}, c={key:'456'};
// b 不是字符串也不是 Symbol 类型,需要转换成字符串。
// 对象类型会调用 toString 方法转换成字符串 [object Object]。a[b]='b';
// c 不是字符串也不是 Symbol 类型,需要转换成字符串。
// 对象类型会调用 toString 方法转换成字符串 [object Object]。这里会把
b 覆盖掉。a[c]='c';
// 输出 c
算法题「旋转数组」
给定一个数组,将数组中的元素向右移动 k 个位置,其中 k 是非负数。
示例 1:输入: [1, 2, 3, 4, 5, 6, 7] 和 k = 3 输出: [5, 6, 7, 1, 2, 3, 4] 解释: 向右旋转 1 步:
[7, 1, 2, 3, 4, 5, 6] 向右旋转 2 步: [6, 7, 1, 2, 3, 4, 5] 向右旋转 3 步: [5, 6, 7, 1, 2,
3, 4]
示例 2:输入: [-1, -100, 3, 99] 和 k = 2 输出: [3, 99, -1, -100] 解释: 向右旋转 1 步: [99,
-1, -100, 3] 向右旋转 2 步: [3, 99, -1, -100]
//思路:直接截取[5,6,7],然后放入数组的前面
function ratate(arr,k){ //k代表后移数量
var array = [...arr] //拷贝数组防止修改arr
var len = array.length
if(k==0) return array
k = k%len //如果k大于等于数组长度,比如k=7相当于k=0
return array.slice(-k).concat(arraty.slice(0,len-k)) //[5,6,7].cancat([1,2,3,4])
}
Vue 的父组件和子组件生命周期钩子执行顺序是什么
1.父组件: beforeCreate -> created -> beforeMount
2.子组件: -> beforeCreate -> created -> beforeMount -> mounted
3.父组件: -> mounted
4.总结:从外到内,再从内到外
创建时:先挂载子组件到父组件上,再挂载父组件。
销毁时:父组件beforeDestory->子组件beforeDestory->子组件destoryed->父组件destoryed
input 搜索如何防抖,如何处理中文输入
<div>
<input type="text" id="ipt">
</div>
<script>
let ipt = document.getElementById('ipt');
let dbFun = debounce()
ipt.addEventListener('keyup', function (e) {
dbFun(e.target.value);
})
function debounce() {
let timer;
return function (value) {
if(timer) clearTimeout(timer);
timer = setTimeout(() => { console.log(value) }, 500);
}
}
</script>
介绍下 Promise.all 使用、原理实现及错误处理
const p = Promise.all([p1, p2, p3]);
Promise.all 方法接受一个数组作为参数,p1、p2、p3 都是 Promise 实例,如果
不是,就会先调用下面讲到的 Promise.resolve 方法,将参数转为 Promise 实例,
再进一步处理。(Promise.all 方法的参数可以不是数组,但必须具有 Iterator 接
口,且返回的每个成员都是 Promise 实例。)
//定义all静态方法,传递一个参数[promise,1,2],数组中的每一项可以是promise,也可以是数字
//只有每一项成功了,才会返回数组, 否则返回失败,而且promise是按顺序执行的
Promise.all = function (promises) {
var arr = [] //返回一个数组
var num = 0;
function precess(key, value) { //用于存放数据的一个方法
arr[key] = value
if (++num == callbacks.length) { //代表所有的任务都执行完毕 promise是异步的
resolve(arr)
}
}
promises.forEach((item, i) => {
if (item.then && typeof item.then == 'function') { //如果是一个promise
item.then(res => { //执行,并把结果放入数组
process(i, res)
}).catch(err){
reject(err) //一旦出错就结束
}
}
else {
process(i, item)
}
});
}