Vue 的响应式原理中 Object.defineProperty 有什么缺陷?
为什么在 Vue3.0 采用了 Proxy,抛弃了 Object.defineProperty?
Object.defineProperty 无法监控到数组下标的变化,导致通过数组下标添
加元素,不能实时响应;以及对象添加了新的属性也不行。
Object.defineProperty 只能劫持对象的属性,从而需要对每个对象,每个
属性进行遍历,如果,属性值是对象,还需要深度遍历。Proxy 可以劫持整个对
象,并返回一个新的对象。
Proxy 不仅可以代理对象,还可以代理数组。还可以代理动态增加的属性。
怎么让一个 div 水平垂直居中
<div class="parent">
<div class="child"></div>
</div>
//flex
.parent {
display:flex;
justify-content:center;
align-items:center
}
//relative,
.parent{
position:'relative',
}
.child{
position:'absolute',
top:50%; //左上角在父亲的中间
left:50%; //左上角在父亲的中间
transform:translate(-50%,-50%) //以自己为中心左移上移自己高度和宽度的50%,两者中心重合,
//对于child的height,width固定的,可以直接使用像素或者margin:auto(会自动计算)
}
//grid
.parent {
display:grid;
}
.child {
justify-self:center;
align-self:center
}
//vertacal-align inline-block
.parent{
text-align:center
}
.child{
display:inline-block,
verticael-align:middle
}
//line-height
.parent{
text-align:center,
height:30px;
line-height:30px;
}
输出以下代码的执行结果并解释为什么
var a = {n: 1};
var b = a;a.x = a = {n: 2};
console.log(a.x)
console.log(b.x)
结果:undefined{n:2}
首先,a 和 b 同时引用了{n:2}对象,接着执行到 a.x = a = {n:2}语句,尽管赋值
是从右到左的没错,但是.的优先级比=要高,所以这里首先执行 a.x,相当于为
a(或者 b)所指向的{n:1}对象新增了一个属性 x,即此时对象将变为
{n:1;x:undefined}。之后按正常情况,从右到左进行赋值,此时执行 a ={n:2}的时
候,a 的引用改变,指向了新对象{n:2},而 b 依然指向的是旧对象。之后执行
a.x = {n:2}的时候,并不会重新解析一遍 a,而是沿用最初解析 a.x 时候的 a,
也即旧对象,故此时旧对象的 x 的值为{n:2},旧对象为 {n:1;x:{n:2}},它被 b
引用着。后面输出 a.x 的时候,又要解析 a 了,此时的 a 是指向新对象的 a,而
这个新对象是没有 x 属性的,故访问时输出 undefined;而访问 b.x 的时候,将
输出旧对象的 x 的值,即{n:2}。
冒泡排序如何实现,时间复杂度是多少, 还可以如何改进?
//常规的冒泡排序
function maopao(arr){
var array = [...arr] //拷贝arr防止修改了原有的arr
var len = array.length
for(var i = 0;i < len-1; i++){ //比较的趟数
for(var j = 0; j < len -1 -i; j++){ //每趟要比较的元素个数,一趟结束后最大值放最后
if(array[j]>array[j+1]){
var temp = array[j]
array[j] = array[j+1]
array[j+1] = temp
}
}
}
return array
}
//分析:对于上面的算法,即使后面的数据有序了,还是会进行比较,可以设置一个变量flag减少比较次数,
并记录上一躺最后一次交换的位置,该位置(从左到右遍历时)后面的元素都是排好序的,反之亦然。
//优化代码:
funtion maopao(arr){
var array = [...arr]
var len = array.length
var lastMinIndex = 0 //上一躺交换位置,从右到左遍历
var lastMaxIndex = len-1 //上一躺交换位置,从左到右遍历
for(var i = 0; i<len-1;i++){ //比较的趟数
var minIndex = 0 , maxIndex = 0 //初始化本躺最后交换位置
var flag = 0 //代表没有交换, 1代表交换
for(var j = lastMinIndex;j<lastMaxIndex;j++){
if(array[j]>array[j+1]){
var temp = array[j]
array[j] = array[j+1]
array[j+1] = temp
flag = 1
maxIndex = j
}
}
if(!flag) break //没有交换说明数据已经排好序
lastMaxIndex = maxIndex
for(var j = lastMaxIndex;j>lastMinIndex;j--){
if(array[j]<array[j-1]){
var temp = array[j]
array[j] = array[j-1]
array[j-1] = temp
flag = 1
minIndex = j
}
}
if(!flag) break //没有交换说明数据已经排好序
lastMinIndex = minIndex
}
return array
}
//测试代码
var arr = []
for (var i = 0; i < 100; i++) {
arr.push(100 - i)
}
console.log('排序前:', arr)
console.log('排序后:', maopao(arr))
某公司 1 到 12 月份的销售额存在一个对象里面
如下:{1:222, 2:123, 5:888},请把数据处理为如下结构:[222, 123, null, null, 888,
null, null, null, null, null, null, null]。
let obj = {1:222, 2:123, 5:888}
let result = Array.from({length:12},(item,index)=>{
return obj[index+1] || null
})
Array.from的三种用法:
1.Array.from(obj,mapFn)
obj指的是数组对象、类似数组对象或者是set对象,mapFn指的是对数组中的元素进行处理的方法。
2.Array.from({length:n},mapFn)
第一个参数指定了第二个参数执行的次数。可以将各种值转化为真正的数组。
3.Array.from(string)
Array.from('abc') //['a','b','c']
要求设计 LazyMan 类,实现以下功能。
LazyMan('Tony');
// Hi I am Tony
LazyMan('Tony').sleep(10).eat('lunch');
// Hi I am Tony // 等待了 10 秒... // I am eating lunch
LazyMan('Tony').eat('lunch').sleep(10).eat('dinner');
// Hi I am Tony // I am eating lunch // 等待了 10 秒... // I am eating diner
LazyMan('Tony').eat('lunch').eat('dinner').sleepFirst(5).sleep(1 0).eat('junk food');
// Hi I am Tony// 等待了 5 秒... // I am eating lunch // I am eating dinner // 等待了 10 秒... // I am eating junk food
//分析:先来的任务先执行,要使用队列queue管理任务,sleepFirst最先执行,说明该任务在队列的头部
class LazyMan(){
constructor(name){
console.log('Hi I am '+name)
this.queue = [] //队列管理任务
//开始调度任务,setTimeout保证后面的任务已经添加进来
setTimeout(()=>{
this.next()
},0)
}
next(){ //取出队头任务并执行,先放进来的先执行
var fn = this.queue.shift()
fn && fn()
}
ear(food){
const fn = ()=>{
console.log('I am eating '+food)
this.next() //不等待直接执行列头函数
}
this.queue.push(fn)
return this //链式调用
}
sleep(time){
const fn = ()=>{
setTimeout(()=>{
console.log('等待了'+time+'秒...')
this.next() //关键点,异步任务执行完了才能执行后面的任务
},time)
}
this.queue.push(fn)
return this //链式调用
}
sleepFirst(time){
const fn = ()=>{
setTimeout(()=>{
console.log('等待了'+time+'秒...')
this.next()
},time)
}
this.queue.unshift(fn) //跟sleep就这里不同,这里优先执行要放在队头
return this //链式调用
}
}
分析比较 opacity: 0、visibility: hidden、display:none 优劣和适用场景。
display: none (不占空间,不能点击)(场景,显示出原来这里不存在的结构)
visibility: hidden(占据空间,不能点击)(场景:显示不会导致页面结构发生变动,不会撑开)
opacity: 0(占据空间,可以点击)(场景:可以跟 transition 搭配)
箭头函数与普通函数(function)的区别是什么?构造函数(function)可以使用 new 生成实例,那么箭头函数可以吗?为什么?
箭头函数是普通函数的简写,可以更优雅的定义一个函数,和普通函数相比,有以下几点差异:
函数体内的 this 对象,就是定义时所在的对象,而不是使用时所在的对象。不可以使用 arguments 对象,该对象在函数体内不存在。如果要用,可以用 rest 参数代替。
不可以使用 yield 命令,因此箭头函数不能用作 Generator 函数。不可以使用 new 命令,因为:
1.没有自己的 this,无法调用 call,apply。
2.没有 prototype 属性 ,而 new 命令在执行时需要将构造函数的 prototype 赋值给新的对象的 __proto__
function newFunc(father, ...rest) {
var result = {};
result.__proto__ = father.prototype;
var result2 = father.apply(result, rest);
if ((typeof result2 === 'object' || typeof result2 === 'function')
&& result2 !== null ) {
return result2;
}
return result;
}
给定两个数组,写一个方法来计算它们的交集
例如:给定 nums1 = [1, 2, 2, 1],nums2 = [2, 2],返回 [2, 2]。
var nums1 = [1,2,2,1], nums2 = [2,2]
var res = []
//对于未排序的两个数组,每次都要循环整个nums2数组 O(n^2)
nums1.forEach(item=>{
var index = nums2.indexOf(item)
if(index!=-1){
res.push(item)
nums2.splice(index,1) //找到了之后要将该元素从数组中清掉,防止重复
}
})
//对于拍好序的两个数组,只需要使用双指针同时遍历,只要一方遍历到了数组最后一个元素就结束 O(n)
var i = 0,j=0
while(i<nums1.length&&j<nums2.length){
if(nums1[i] == nums2[j]){ //同时后移指针
res.push(nums1[1])
i++
j++
}
if(nums1[i] > nums2[j]){ //小的一方后移指针
j++
}
if(nums1[i] < nums2[j]){ //小的一方后移指针
i++
}
}
已知如下代码,如何修改才能让图片宽度为 300px ?注意下面代码不可修改。
<img src="1.jpg" style="width:480px!important" />
max-width: 300px; //优先级高于width
transform: scale(0.625,0.625) //按比例缩小