• 18.2.28阿里前端实习生内推面补坑


    接到电话在外面,在路边面了15分钟,可以说发挥的烂透了。。。但是面试的小姐姐的声音巨好听........这里记录下答的不好的点,现在补上。o(╥﹏╥)o o(╥﹏╥)o o(╥﹏╥)o

    一.js中遍历一个数组有多少种方法?

    1.普通for循环:

    for(j = 0; j < arr.length; j++) {
       
    } 
    

    2.for循环优化版

    for(j = 0,len=arr.length; j < len; j++) {
       
    }
    

    提前用临时变量把长度存起来,避免每次循环都要进行获取数组长度的操作,当数组较大时优化效果才会比较明显。这种方法基本上是所有循环遍历方法中性能最高的一种

    3.for循环弱化版

    for(j = 0; arr[j]!=null; j++) {
       
    }
    

    这种方法严格上也属于for循环,只不过是没有使用length判断,而使用变量本身判断
    实际上,这种方法的性能要远远小于普通for循环

    4.forEach循环

    数组自带的方法,使用频率较高,实际上性能比普通for循环弱。

    需要注意的一点是函数里面的上下文是Array,如果在里面用async函数里面遍历数组,在forEach里面对元素进行await声明,就会报错。

    await 的执行上下文必须是 async 函数

    // 错误写法
    async function forDemo() {
        let arr = [1, 2, 3, 4];
        arr.forEach(item => {
            let tmp = await item;
            console.log(tmp);
        });
    }
    
    forDemo();
    // 正确写法
    async function forDemo01() {
        let arr = [1, 2, 3, 4];
        for (let i = 0; i < arr.length; i ++) {
            let tmp = await arr[i];
            console.log(tmp);
        }
    }
    
    forDemo01();
    

    关于forEach的详细,看文档:链接

    5.forEach变种

    Array.prototype.forEach.call(arr,function(el){  
       
    });
    

    由于foreach是Array型自带的,对于一些非这种类型的,无法直接使用的伪数组(如HTMLCollection,NodeList),所以才有了这个变种,使用这个变种可以让类似的数组拥有forEach功能。

    实际性能要比普通foreach弱

    6.for in 和for of循环

    这个当时其实关键点答出来了,o(╥﹏╥)o,我知道这个of和in的区别,当时答出了in会遍历出自定义属性,但是感觉没说好~~~~~o(╥﹏╥)o

    先是in:

    for(j in arr) {
       
    }
    

    这个循环很多人爱用,但实际上,经分析测试,在众多的循环遍历方式中
    它的效率是最低的

    然后for of,这个是es6新增的

    for(let value of arr) {  
       
    });
    

    先用具体一个例子,看看两个区别,我们定义一个数组
    let aArray = ['a',123,{a:'1',b:'2'}]

    in循环:
    这里写图片描述

    of循环:
    这里写图片描述

    现在好像没什么区别,我们再给数组增加一个属性,aArray.add = 'momoda'
    我们现在遍历这个数组的key,正常来说是0,1,2.。。。
    但是:
    这里写图片描述
    如图把add也遍历出来了,这样可能在一些场景里不适合,但是用of
    这里写图片描述

    可以看到,of遍历的是键值对中的"value值",而且不会遍历出新增的自定义属性。

    那么结论:

    1. 推荐在循环对象属性的时候,使用for...in,在遍历数组的时候的时候使用for...of。

    2. for...in循环出的是key,需要Array[key]访问value,for...of循环出的是value,但是不会遍历出新增属性。

    3. 注意,for...of是ES6新引入的特性。修复了ES5引入的for...in的不足

    4. for...of不能循环普通的对象,需要通过和Object.keys()搭配使用
      这里写图片描述
      注意别把数组里面key和对象的key搞混淆了,数组里面的key就是下标。

    for...of不能循环遍历普通对象,对普通对象的属性遍历推荐使用for...in

    如果实在想用for...of来遍历普通对象的属性的话,可以通过和Object.keys()搭配使用,先获取对象的所有key的数组
    然后遍历:

    var foo = {
        name:'zmz',
        age:18,
        locate:{
        country:'china',
        Planet:'earth'
        }
    }
    for(var key of Object.keys(foo)){
        //使用Object.keys()方法获取对象key的数组
        console.log(key+": "+student[key]);
    }
    

    for...of文档:链接

    7.map()

    这个最气,js map的功能是和python的一样的,当时外面玩了几天状态不在线,大脑都是懵了,这个竟然都没说出来,气o(╥﹏╥)o。
    参数:
    callback
    生成新数组元素的函数,使用三个参数:
    currentValue
    callback 的第一个参数,数组中正在处理的当前元素。
    index
    callback 的第二个参数,数组中正在处理的当前元素的索引。
    array
    callback 的第三个参数,map 方法被调用的数组。
    thisArg
    可选的。执行 callback 函数时 使用的this 值。
    和foreach差不多,
    这里写图片描述
    其实二题的答案面试官就是想让用map做,map和forEach的不同就是map有返回值,可以把结果返回出来,这些可以在函数做一些计算处理。

    另外一点注意的就是不管是forEach还是map在IE6-8下都不兼容(不兼容的情况下在Array.prototype上没有这两个方法),那么需要我们自己封装一个都兼容的方法:

    Array.prototype.myForEach = function myForEach(callback,context){  
        context = context || window;  
        if('forEach' in Array.prototye) {  
            this.forEach(callback,context);  
            return;  
        }  
        //IE6-8下自己编写回调函数执行的逻辑  
        for(var i = 0,len = this.length; i < len;i++) {  
            callback && callback.call(context,this[i],i,this);  
        }  
    } 
    

    总结

    以上大概就是现在能想到的所有方法,欢迎补充,性能方便经测试在chrome环境下,for.....in循环最慢。优化后的普通for循环最快。

    二.把数组中元素转为字符串?(提示map?)

    var arr = [1,2,3,4];
    var ans = arr.map(function(value){
        return value.toString();
    })
    

    map() 方法创建一个新数组,其结果是该数组中的每个元素都调用一个提供的函数后返回的结果。

    map 方法会给原数组中的每个元素都按顺序调用一次 callback 函数。callback 每次执行后的返回值(包括 undefined)组合起来形成一个新数组。 callback 函数只会在有值的索引上被调用;那些从来没被赋过值或者使用 delete 删除的索引则不会被调用。

    map文档:链接

    三.rem布局原理

    谈到rem的话,就要和em一起说说。

    rem和em两个都是css的单位,并且也都是相对单位,现有的em,css3才引入的rem。

    em作为font-size的单位时,其代表父元素的字体大小,em作为其他属性单位时,代表自身字体大小——MDN

    关于em的一个题目:

    <div class="p1">
    	<div class="s1">1</div>
      	<div class="s2">1</div>
    </div>
    <div class="p2">
    	<div class="s5">1</div>
      	<div class="s6">1</div>
    </div>
    
    
    .p1 {font-size: 16px; line-height: 32px;}
    .s1 {font-size: 2em;}
    .s2 {font-size: 2em; line-height: 2em;}
    
    .p2 {font-size: 16px; line-height: 2;}
    .s5 {font-size: 2em;}
    .s6 {font-size: 2em; line-height: 2em;}
    

    答案:
    p1:font-size: 16px; line-height: 32px
    s1:font-size: 32px; line-height: 32px
    s2:font-size: 32px; line-height: 64px

    • p1 无需解释
    • s1 em作为字体单位,相对于父元素字体大小;line-height继承父元素计算值
    • s2 em作为行高单位时,相对于自身字体大小

    p2:font-size: 16px; line-height: 32px
    s5:font-size: 32px; line-height: 64px
    s6:font-size: 32px; line-height: 64px

    • p2 line-height: 2自身字体大小的两倍
    • s5 数字无单位行高,继承原始值,s5的line-height继承的2,自身字体大小的两倍
    • s6 无需解释

    rem作用于非根元素时,相对于根元素字体大小;rem作用于根元素字体大小时,相对于其出初始字体大小——MDN

    /* 作用于根元素,相对于原始大小(16px),所以html的font-size为32px*/
    html {font-size: 2rem}
    
    /* 作用于非根元素,相对于根元素字体大小,所以为64px */
    p {font-size: 2rem}
    

    rem布局的本质是等比缩放,一般是基于宽度,试想一下如果UE图能够等比缩放,那该多么美好啊

    假设我们将屏幕宽度平均分成100份,每一份的宽度用x表示,x = 屏幕宽度 / 100,如果将x作为单位,x前面的数值就代表屏幕宽度的百分比

    p { 50x} /* 屏幕宽度的50% */ 
    

    如果想要页面元素随着屏幕宽度等比变化,我们需要上面的x单位,不幸的是css中并没有这样的单位,幸运的是在css中有rem,通过rem这个桥梁,可以实现神奇的x

    通过上面对rem的介绍,可以发现,如果子元素设置rem单位的属性,通过更改html元素的字体大小,就可以让子元素实际大小发生变化

    html {font-size: 16px}
    p { 2rem} /* 32px*/
    
    html {font-size: 32px}
    p { 2rem} /*64px*/
    

    如果让html元素字体的大小,恒等于屏幕宽度的1/100,那1rem和1x就等价了

    html {fons-size: width / 100}
    p { 50rem} /* 50rem = 50x = 屏幕宽度的50% */ 
    
    

    如何让html字体大小一直等于屏幕宽度的百分之一呢? 可以通过js来设置,一般需要在页面dom ready、resize和屏幕旋转中设置

    document.documentElement.style.fontSize = document.documentElement.clientWidth / 100 + 'px'; 
    

    上面提到想让页面元素随着页面宽度变化,需要一个新的单位x,x等于屏幕宽度的百分之一,css3带来了rem的同时,也带来了vw和vh

    vw —— 视口宽度的 1/100;vh —— 视口高度的 1/100 —— MDN

    这个其实等价于上面提到的x,
    根据定义可以发现1vw=1x,有了vw我们完全可以绕过rem这个中介了,下面两种方案是等价的,可以看到vw比rem更简单,毕竟rem是为了实现vw

    /* rem方案 */
    html {fons-size: width / 100}
    p { 15.625rem}
    
    /* vw方案 */
    p { 15.625vw}
    vw还可以和rem方案结合,这样计算html字体大小就不需要用js了
    
    html {fons-size: 1vw} /* 1vw = width / 100 */
    p { 15.625rem}
    

    当然越高级的东西,兼容性就越不好了,vw要求的版本就比rem更高一点。
    另外,在使用弹性布局时,一般会限制最大宽度,比如在pc端查看我们的页面,此时vw就无法力不从心了,因为除了width有max-width,其他单位都没有,而rem可以通过控制html根元素的font-size最大值,而轻松解决这个问题。

    最后一句:rem是弹性布局的一种实现方式,弹性布局可以算作响应式布局的一种,但响应式布局不是弹性布局,弹性布局强调等比缩放,100%还原;响应式布局强调不同屏幕要有不同的显示。在很多场景都有不同情景要求。

    四.vue原理

    传统的MV*模型。我们需要编写代码,将从服务器获取的数据进行“渲染”,展现到视图上。每当数据有变更时,我们会再次进行渲染,从而更新视图,使得视图与数据保持一致,而另一方面,页面也会通过用户的交互,产生状态、数据的变化,这个时候,我们则编写代码,将视图对数据的更新同步到数据,以致于同步到后台服务器.这两种过程都是单向的。

    VueJS 则使用 ES5 提供的 Object.defineProperty() 方法,监控对数据的操作,从而可以自动触发数据同步。并且,由于是在不同的数据上触发同步,可以精确的将变更发送给绑定的视图,而不是对所有的数据都执行一次检测。

    数据与视图的绑定与同步,最终体现在对数据的读写处理过程中,也就是 Object.defineProperty() 定义的数据 set、get 函数中。Vue 中对于的函数为 defineReactive:

    Vue 双向数据绑定实现

    function defineReactive(obj, key, value) {
        var dep = new Dep()
        Object.defineProperty(obj, key, {
            enumerable: true,
            configurable: true,
            get: function reactiveGetter() {
                if (Dep.target) {
                    dep.depend()
                }
                return value
            },
            set: function reactiveSetter(newVal) {
                if (value === newVal) {
                    return
                } else {
                    value = newVal
                    dep.notify()
                }
            }
        })
    }
    

    在对数据进行读取时,如果当前有 Watcher(对数据的观察者吧,watcher 会负责将获取的新数据发送给视图),那将该 Watcher 绑定到当前的数据上(dep.depend(),dep 关联当前数据和所有的 watcher 的依赖关系),是一个检查并记录依赖的过程。而在对数据进行赋值时,如果数据发生改变,则通知所有的 watcher(借助 dep.notify())。这样,即便是我们手动改变了数据,框架也能够自动将数据同步到视图。

    这里写图片描述

    数据绑定关系的识别过程

    Vue 和 AngularJS 中,都是通过在 HTML 中添加指令的方式,将视图元素与数据的绑定关系进行声明。例如:

    <form id="test">
      <input type="text" v-model="name">
    </form>
    

    以上的 HTML 代码表示该 input 元素与 name 数据进行绑定。在 JS 代码中可以这样进行初始化:

    var vm = new Vue({
      el: '#test',
      data: {
        name: 'luobo'
      }
    })
    

    代码正确执行后,页面上 input 元素对应的位置会显示上面代码中给出的初始值:luobo。

    由于双向数据绑定已经建立,因此:

    • 执行 vm.name = 'mickey' 后,页面上 input 也会更新为显示: mickey
    • 在页面文本框中修改内容为:tang,则通过vm.name 获取的值为:"tang"

    那么初始化的过程中,Vue 是如何识别出这种绑定关系的呢?

    通过分析源码,在初始化过程中(new Vue() 执行时),主要执行两个步骤:

    • compile
    • link

    compile 过程中,对于给定的目标元素进行解析,识别出所有绑定在元素(通过 el 属性传入)上的指令。
    link 过程中,建立这些指令与对应数据(通过 data 属性传入初始值)的绑定关系,并以数据的初始值进行渲染。绑定关系建立后,就可以双向同步数据了。

  • 相关阅读:
    主数据管理(MDM)的成熟度
    Tensorflow实战Google深度学习框架—郑泽宇书籍整理
    《Flink基础教程》王绍学习资料
    《重新定义计算(ApacheFlink实践)》_蒋晓伟资料整理
    js实现页面的自定义翻译
    谈谈px,em,rem(采自菜鸟)
    js和css分别实现元素曲线运动
    echarts鼠标事件以及自定义数据获取
    js数组的多条件排序
    H5-meta标签使用大全
  • 原文地址:https://www.cnblogs.com/zhangmingzhao/p/8496045.html
Copyright © 2020-2023  润新知