iOS面试的一些小问题

React vs Vue

前言:

  1. 技术选型没有绝对的对与错
  2. 技术选型要考虑的因素非常多
  3. 表达自己的观点,并说明理由

两者的本质区别:

  1. vue - 本质是MVVM框架,由mvc发展而来
  2. React - 本质是前端组件化框架,由后端组件化发展而来
  3. 但两者都能实现相同的功能

模板的区别:

  1. vue - 使用模板(template)由angular提出

  2. React - 使用JSX,一种编程语法,类似于html

  3. 模板语法上更倾向于JSX:

    • vue中的指令v-需要现学,并且它们的值是放在引号中,比较困惑;
    • 而JSX中,只需要知道大括号中可以写JS表达式,就OK
  4. 模板分离上更倾向于vue

组件化的区别:

  1. React本身就是组件化,没有组件化就不是React
  2. vue也支持组件化,不过是在MVVM上的扩张
  3. 对于组件化,更倾向于React,做的更彻底

两者共同点:

  1. 支持组件化
  2. 数据驱动视图

总结:

1.国内,首推vue,文档易读、易学、社区足够大

2.团队水平高,推荐使用React,组件化和jsx

作为一个开发者,有一个学习的氛围跟一个交流圈子特别重要,这是一个我的iOS开发交流群:130595548,不管你是大牛还是小白都欢迎入驻 ,让我们一起进步,共同发展!(群内会免费提供一些群主收藏的免费学习书籍资料以及整理好的几百道面试题和答案文档!)

React

对组件化的理解

与面向对象类似,分为: 封装、继承、多态

组件的封装

  1. 视图
  2. 数据
  3. 变化逻辑(数据驱动视图变化)

分装完成后使用者不用明白组件中的视图、数据、变化逻辑,只需要提供组件需要的数据即可

组件的复用

  1. props传递(传递不同的数据,组件产生不同的效果,但模板是一样的)
  2. 复用

JSX的本质

React引入的一种编程的语法,类似于html

JSX语法

JSX语法无法被浏览器直接解析,最终需要转换成JS来运行

  1. html(普通标签)
  2. 引入JS变量和表达式
  3. if判断
  4. for循环
  5. style 和 calssName
  6. 事件绑定

JSX解析(本质)

JSX是语法糖,需被解析成JS才能运行,通过React.createElement(‘tagName’, {标签属性}, 子元素…),可以联想到vdom中的h函数(生成vnode)

  1. JSX其实是语法糖
  2. 开发环境会将JSX编译成JS代码
  3. JSX的写法大大降低了学习成本和编码工作量

JSX已经标准化

  1. JSX是React引入的,但不是React独有的
  2. JSX其它项目也可以使用
  3. React.createElement是可以自定义修改的
  4. 说明: 本身功能已完备,兼容和扩展性没问题

JSX和vdom的关系

为何需要vdom

  1. vdom是React初次推广开的,结合JSX来使用
  2. JSX就是模板(类似于vue模板),最终需要渲染成html
  3. 初次渲染+修改state后的re-render
  4. 正好符合vdom的应用场景

React.createElement() 和 h()函数

所有的JSX代码都会解析成JS代码来执行,JS代码执行时会返回vnode

何时patch

  1. 初次渲染 - ReactDOM.render(, container),会触发patch(container, vnode)
  2. re-render - setState时会触发patch(vnode, newVnode)进行对比

自定义组件的处理

  1. 'div’会直接渲染成<div>
  2. Input和List,是自定义组件class,vdom默认不认识
  3. 因此,Input和List定义时必须声明render函数
  4. 并根据props初始化实例,染回执行实例的render函数
  5. render函数返回的是一个vnode对象
return React.createElement('div', null, 
  React.createElement(Input, {addTitle: this.addTitle.bind(this)}),
  React.createElement(List, {data: this.state.list}),
  React.createElement('p', null, 'this is demo')
)
// React.createElement(List, {data: this.state.list}), 类似以下代码
const list = new List({data: this.state.list})
const vnode = list.render()

总结

  • 为何需要vdom: JSX需要渲染成html,并通过vdom数据驱动视图操作、渲染html
  • React.createElement和h函数一样,都生成vnode
  • 何时patch:ReactDOM.render()初次渲染和setState后续更新
  • 自定义组件的解析:初始化实例,然后执行render函数返回vnode

React 生命周期

Mounting 阶段

Mounting阶段叫挂载阶段,伴随着整个虚拟DOM的生成,它里面有三个生命周期函数,分别是:

  1. componentWillMount: 在组件被挂载到页面之前执行
  2. render: 页面初始化、state和props发生变化时执行
  3. componentDidMount: 组件挂载完成时被执行
componentWillMount:

此函数只在组件初始化时或页面刷新时执行一次。

render:

render()是纯函数,只返回需要渲染的东西,不应该包含其它的业务逻辑,可以返回原生DOM、React组件、Fragment、字符串、数字、Boolean和null等内容。

componentDidMount:

此函数只在组件初始化时或页面刷新时执行一次。

组件装载之后调用,此时可以获取到DOM节点并操作,比如对canvas、svg的操作,服务器请求,订阅都可以写在里面,但是记得在componentWillUnmount中取消订阅

Updation 更新阶段

Updation阶段,也是组件发生改变的更新阶段,它有两个基本部分组成,一是props属性改变,二是state状态改变

  1. shouldComponentUpdate: 在组件更新之前,自动被执行
  2. componentWillUpdate: 在函数shouldComponentUpdate返回true后执行
  3. render()
  4. componentDidUpdate: 在组件更新之后执行,它是组件更新的最后一个环节
shouldComponentUpdate:

shouldComponentUpdate(nextProps, nextState),有两个参数nextProps和nextState,表示新的属性和变化之后的state,返回一个布尔值,true表示会触发重新渲染,false表示不会触发重新渲染,默认返回true,通常利用此生命周期来优化React程序性能

render

更新阶段也会触发此函数

componentDidUpdate:

componentDidUpdate(preProps, preState, snapshot),该方法有三个参数,之前的props、state以及snapshot 组件更新之后执行,它是组件更新的最后一个环节

Unmounting 卸载阶段

  1. componentWillUnmount: 当我们的组件被卸载或销毁时就会调用
componentWillUnmount:

在组件被销毁之前可以在函数中执行,去除定时器,取消网络请求,清理无效的DOM元素等垃圾清理工作

生命周期改善程序性能

通过 shouldComponentUpdate 函数改善React组件的性能
// nextProps: 变化后的属性,nextState: 变化后的状态
shouldComponentUpdate(nextProps, nextState) {
    if(nextProps.content !== this.props.content) {
        return true
    }else {
        return false
    }
}

基础

base

  1. web标准

    • 结构: 用于结构化的web标准化技术主要由Html、Xhtml、Xml
    • 表现: 主要是CSS
    • 行为: 体现行为的标准技术主要有Dom和Javascript
  2. 主流浏览器及内核

    • Chrome - Blink
    • Safari - Webkit
    • Firefox - Gecko
    • IE - Trident
    • Opera - Blink
    • 百度、世界之窗 - IE内核
    • 360、猎豹浏览器 - IE+Chrome 双内核
    • 搜狗、QQ浏览器 - Trident(兼容模式)+Webkit(高速模式)

HTML

DOCTYPE的作用

DOCTYPE是html5标准网页声明,且必须声明在html文档的第一行<!DOCTYPE html>。作用是通知浏览器的解析器使用什么文档标准解析此文档

文档解析类型有:
  • BackCompat: 怪异模式,默认使用此模式
    • 会模拟更旧的浏览器的行为
  • CSS1Compat: 标准模式,浏览器使用W3C的标准解析渲染页面
    • 页面按照HTML和CSS的定义渲染

HTML、XHTML、XML的区别

  • HTML(超文本标记语言): 在html4.0之前HTML现有实现再有标准,导致HTML非常混乱松散
  • XML(可扩展标记语言): 主要用于存储数据和结构,可扩展,作用与JSON类似
  • XHTML(可扩展超文本标记语言): 基于上面两者而来,W3C为解决HTML混乱问题而生,基于此诞生了HTML5,开头加入<!DOCTYPE html>,就是标准模式

HTML语义化的理解

语义化是指使用恰当的html标签,让页面具有良好的结构与含义,比如<p>代表段落,<article>代表正文内容等。

语义化的好处:
  • 开发者友好: 使用语义类标签增强可读性,开发者可以清晰的看出网页结构,便于团队的开发和维护
  • 机器友好: 语义类标签,更适合搜索引擎的爬虫爬取有效的信息
  • 对于知乎、掘金此类富文本类的应用很重要,语义化对于其网站的内容传播有很大的帮助

meta标签

meta标签由name和content两个属性来定义,来描述一个HTML网页文档的属性,如作者、时间、网页描述、关键词、页面刷新等,除了http规定的一些name外,开发者也可以自定义name

  • charset:用于描述HTML文档的编码形式

    <meta charset="UTF-8" >
    
  • http-equiv:相当于http的文件头作用,如下可以设置http的缓存过期日期

    <meta http-equiv="expires" content="Wed, 20 Jun 2019 22:33:00 GMT" >
    
  • viewport:移动端中,开发人员可以控制视口的大小和比例

    <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1" >
    
  • apple-mobile-web-app-status-bar-style:开发过PWA应用的开发者应该很熟悉,为了自定义评估工具栏的颜色

    <meta name="apple-mobile-web-app-status-bar-style" content="black-translucent" >
    

src和href的区别

  • src是指向外部资源的位置,指向的内容会嵌入到文档中当前标签所在的位置,在请求src资源时会将其指向的资源下载并应用到文档内,如JS脚本、img图片、frame等元素。当浏览器解析到该元素时,会暂停其它资源的下载和处理,直到该资源加载、编译、执行完毕,所以一般JS脚本会放在底部而不是头部
  • href是指向网络资源所在位置(超链接),用来建立和当前元素、文档之间的连接,当浏览器识别到它指向的文件时,就会并行下载资源,不会停止对当前文档的处理

img中srcset的作用

srcset提供了根据屏幕条件选取图片的能力

可以设计响应式图片,我们可以使用两个新的属性srcset和sizes来提供更多额外的资源图像和提示,帮助浏览器选择一个正确的资源 srcset定义了我们允许浏览器选择的图像集,以及每个图像的大小。 sizes定义了一组媒体条件(如屏幕宽度)并且指明当某些媒体条件为真时,什么样的图片尺寸为最佳选择。

有了以上属性,浏览器会:

  • 查看设备宽度

  • 检查sizes列表中那个媒体条件是第一个为真

  • 查看给与该媒体查询的槽大小

  • 加载srcset列表中引用的最接近所选的槽大小的图像

    <img 
        src="clock-demo-thumb-200.png"
        alt="clock"
        srcset="clock-demo-thumb-200.png 200w,
                clock-demo-thumb-400.png 400w"
        sizes="(min-width:600px) 200px, 50vw" />
    

script标签中defer和async的区别

  • defer: <script defer>浏览器指示脚本在文档被解析后执行,script被异步加载后并不会立即执行,而是等待文档被解析完毕后执行
  • acync: <script async>同样是异步加载,区别是脚本加载完毕后立即执行,这导致async属性下的脚本是乱序的,对于script有先后依赖关系的情况,并不适用
  • 总结: defer是异步加载,并等到文档解析完毕后才执行;async也是异步加载,并且加载完毕后立即执行,在项目中一般将script脚本放在文档最后

前端存储方式

  • cookies: 在HTML5之前本地存储的主要方式

    • 优点: 兼容性好,请求头自带cookies方便
    • 缺点: 大小只有4k容量小,每次请求都自带cookies整体浪费流量
  • localStorage: HTML5加入的以键值对(key:value)为标准的存储方式

    • 优点: 操作方便,永久性存储(5M),兼容IE8+
  • sessionStorage: 与上边类似,区别是sessionStorage当页面关闭后会被清理,与cookies和localStorage不同,不能再所有同源窗口*享,是会话级别的存储方式

  • IndexedDB: 被正式纳入HTML5标准的数据库存储方案,是NoSQL数据库,使用键值对进行存储,可进行快速读取操作,非常适用于web场景

CSS

CSS选择器的优先级

CSS选择器的优先级是: 内联样式>ID选择器>类选择器>标签选择器

  • 内联 - 1.0.0.0
  • ID选择器 - 0.1.0.0
  • 类选择器 - 0.0.1.0
  • 标签选择器 - 0.0.0.1

同类型的选择器会相加,但没有进位

link和@import的区别

  • link属于XHTML标签,而@import是css提供的
  • 页面被加载时,link会同时被加载,而@import引用的css会等到页面加载完成后再加载
  • import只能在IE5中识别,而link是XHTML标签,无兼容问题
  • link方式的样式权重高于@import的权重

隐藏页面元素(CSS)

  • opacity: 0; 本质是将元素透明,依然占据空间且可以交互
  • visibility: hidden; 隐藏元素,占据空间,不可以交互
  • overflow: hidden; 只隐藏元素中内容溢出的部分,占据空间,不可交互
  • display: none; 将元素从文档流中去除,不占据空间也不能交互
  • z-index: -9999; 原理是将元素的层级放在最底层,被其它元素覆盖
  • transform: scale(0,0); 将元素缩放为0,占据空间,不能交互
  • transform: translate(-1000px); 将元素移到可视区域外

px/em/rem 的区别

  • px: 绝对单位,页面按精确像素展示
  • em: 相对单位,基准点为父元素字体的大小,如果自身定义了font-size按自身来计算,整个页面内的1em不是一个固定值
  • rem: 相对单位,基准点永远是html中的font-size,整个页面的1rem是一个固定值,CSS3新属性

z-index 的理解

控制设置了position属性元素的垂直叠加顺序,元素默认的值为0,通过修改z-index来控制元素图层的位置

  • 2层
  • 1层
  • 0层,默认
  • -1层
  • -2层
  • -…

层叠上下文

定义:

是HTML元素的三维概念,是一个假想的相对于电脑屏幕或网页的Z轴上的延伸,HTML元素根据其自身属性按照优先级占用层叠上下文的空间

产生:
  • z-index 的值不为auto的relative和absolute和fixed
  • opacity 属性值小于1的元素
  • transform 属性值不为none的元素

清除浮动的方法

  • 添加额外标签-clear: both;

    <div>
        // ...
        <div style="clear: both;"></div>
    </div>
    
  • 父级添加overflow属性(BFC)或设置高度

    <div style="overflow: hidden;">
        // ...
    </div>
    
  • 为类选择器清除浮动(推荐)

    .box:after {
        content: '';
        display: block;
        height: 0;
        visibility: hidden;
        clear: both;
    }
    <div class="box">
        // ...
    </div>
    

盒模型的理解

盒模型由content、padding、border、margin组成

定义:

当对一个文档进行布局时,浏览器的渲染引擎会根据标准之一的CSS基础盒模型,将所有元素表示为一个个矩形盒子(box)。CSS决定了这些盒子的大小、位置以及属性(颜色、背景、边框等)

标准盒模型与怪异盒模型的区别

标准盒模型

在W3C标准下,我们定义的width === content的宽度值,height === content的高度值,因此,元素的宽度为:

元素宽度 = margin-left+margin-right+border-left+border+right+padding-left+padding-right+width

怪异盒模型

在IE怪异盒模型(IE8以下)width !== content的宽度,而是 width === border-left+border-right+padding-left+padding-right+width

元素宽度 = margin-left+margin-right+width

更改盒模型
 box-sizing: content-box; // 标准盒模型
 box-sizing: border-box; // 怪异盒模型

BFC 的理解

块级格式上下文: 它是一块独立的区域,让处于BFC内部的元素与外部的元素互相隔离

触发条件
  • 根元素,即HTML元素
  • position: fixed/absolute;
  • float不为none
  • voerflow不为visible
  • display的值为 inline-block、table-cell、table-caption
应用场景
  • 防止浮动导致父元素高度塌陷

    <style>
     .container {
        border: 10px solid red;
        overflow: hidden;
    }
     .inner {
        float: left;
        background: #08BDEB;
        height: 100px;
        width: 100px;
    }
    </style>
    <div class="container">
        <div class="inner">
            // ...
        </div>
    </div>
    
  • 避免外边距折叠

    <style>
     .container {
        background-color: green;
        overflow: hidden;
     }
     .inner {
      background-color: lightblue;
      margin: 10px 0;
     }
     .bfc{
      overflow: hidden;
     }
    </style>
    <div class="container">
    <div class="inner">1</div>
    <div class="bfc">
      <div class="inner">2</div>
    </div>
    <div class="inner">3</div>
    </div>
    

translate和position改变元素位置的区别

  1. translate是transform的一个值,改变transform或opacity不会触发浏览器的重新布局reflow或重绘repaint
  2. 改变绝对定位会触发浏览器的重新布局,进而触发重绘
  3. transform使浏览器为元素创建一个GUP图层,但改变绝对定位会使用CPU
  4. 使用translate更高效,可以缩短拼花动画的绘制时间

伪类和伪元素的区别

伪类: 以冒号:作为前缀,被添加到一个选择器末尾的关键字,当你希望样式在特定状态下才被呈现到指定的元素时,你可以在元素的选择器后面添加对应的伪类

  • 元素:first-child {}
  • 元素:nth-child(n) {}
  • 元素:hover {}
  • 元素:focus {}
  • a:link {未访问的链接}
  • a:visited {以访问的链接}
  • a:active {选定的链接}

伪元素: 用于创建一些不在文档中的元素,并为其添加样式

  • ::before
  • ::after
区别
  • 伪类是通过在元素选择器上加入伪类改变元素的状态
  • 伪元素是通过对元素的操作来添加元素

Less

Less预处理器: 支持CSS语法,引入变量,混合,运算,嵌套等,简化CSS编写,降低维护成本

变量
  • 语法: @变量名: 值;

    @sz: 1000px;
    p {
        font-size: @sz;
        span {
            // 运算
            font-size: @sz - 2;
        }
    }
    
嵌套
.box {
    background-color: #0f0;
    ul {
       list-style: none; 
    }
}
混合(函数)
  1. 定义: #函数名(){} || .函数名(){}
  2. 调用: #函数名 || .函数名
  3. 传参、默认值
    // 1\. 定义
    #public() {
        width: 200px;
        height: 200px;
    }
    .origin() {
        width: 200px;
        height: 200px;
    }
    // 2\. 调用
    div {
        #public()
    }
    h2 {
        .public()
    }
    // 3\. 传参、默认值
    #base(@w:200px, @h:200px) {
        width: @w;
        height: @h;
    }
    div {
        #base(600px, 600px);
    }

Sass

变量
  • 语法: $blue: #1875e7;
$blue: #1875e7;
div {
    color: $blue;
}
  • 嵌套在字符串中,必须写在#{}
$side: left;
.rounded {
    border-#{$side}-radius: 5px;
}
计算功能
body {

}

JS

JS基础

谈谈你对原型链的理解

此问题关键在于两点: 原型对象,原型链的形成

  • 原型对象

    • 大部分函数都有一个prototype属性,此属性是原型对象用来创建新对象实例,而所有被创建的对象都会共享原型对象,因此这些对象便可以访问原型对象的属性
    • 例如hasOwnProperty()方法存在于Object原型对象中,因此它便可以被任何对象当做自己的方法使用
    obj.hasOwnProperty(key)
    // 如果对象obj包含key属性则返回true否则false
    
  • 原型链

    • 原因是每个对象都有__proto__属性,此属性指向该对象的构造函数的原型
    • 对象可以通过__proto__与上游的构造函数的原型对象连接起来,而上游的原型对象也有一个__proto__,这样就形成了原型链

判断一个变量是否是数组

  • Array.isArray(arr)

    if(Array.isArray(value)) { return true }
    
  • toString(arr)

    if(!Array.isArray) {
        Array.isArray = function(args) {
            return Object.prototype.toString.call(args) === "[object Array]"
        }
    }
    

ES6模块与CommonJS模块的区别

  • ES6 Module和CommonJS模块的不同点:
  1. CommonJS是对模块的浅拷贝,ES6 Module是对模块的引用,即ES6 Module是只读的,不能改变其值,就是指针指向不能变
  2. import的接口是只读状态,不能修改其变量值。即不能修改其变量的指针指向,但可以改变变量内部指针指向;可以对CommonJS重新赋值(改变指针指向),但对ES6 Module赋值会编译报错
  • ES6 Module和CommonJS模块的共同点:
  1. CommonJS和ES6 Module都可以对引入的对象进行赋值,即对对象内部属性进行改变

async/await

async函数,就是Generator函数的语法糖,它建立在Promise上,并且与所有的基于Promise和API兼容

  1. async 声明一个异步函数
  2. 自动将常规函数转换成Promise,返回值是一个promise对象
  3. 只有async函数内部的异步操作执行完,才会执行then方法指定的回调函数
  4. async函数内部可以使用await

await暂停异步的功能执行

  1. 放置在Promise调用之前,await强制后面的代码等待,直到Promise完成并返回结果
  2. 只能与Promise一起使用,不适用于回调
  3. 只能用于async函数内部

async/await相对于Promise的优势

  • 代码类似于同步代码,Promise虽然摆脱了回调地狱,但.then的链式调用也会带来额外的阅读负担
  • Promise传递中间值非常麻烦,而async/await几乎是同步写法
  • 错误处理更友好,async/await使用try/catch,Promise使用.catch非常冗余
  • 调试更友好,在.then使用调试器后,调试器并不能进入后续的.then代码块,因为调试器只能跟踪同步代码

JavaScript 的参数传递方式

  • 基本类型传递方式

    JS中基本类型数据是按照值传递

    var a = 1
    function test(x) {
        x = 10
        console.log(x)
    }
    test(a) // 10
    console.log(a) // 1
    
  • 复杂类型按引用传递

    function test(person) {
        person.age = 26;
        person = {
            name: 'ijk',
            age: 123
        }
        return person
    }
    const p1 = {name: 'xyz', age: 20}
    const p2 = test(p1)
    console.log(p1) // {name: 'xyz', age: 26}
    console.log(p2) // {name: 'ijk', age: 123}
    
  • 按共享传递

    复杂类型之所以会产生这种特性,原因就是在传递过程中,对象p1先产生一个副本p1,这个副本p1并不是深克隆得到的副本p1,副本p1地址同样指向对象p1指向的堆内存。

    因此,在函数中修改person只是修改了副本p1,p1对象没有变化,但是如果修改了person.age = 26是修改了两者指向的统一个堆内存,此时p1也会受到影响。

    这种特性叫做传递引用按共享传递

BingInt 提案

  • JS中最大安全数字Number.MAX_SAFE_INTEGER === 2^56,即在此数范围内不会出现精度丢失(小数除外)。

  • 但是一旦超过此范围,JS就会出现计算不准确的情况,使得在大数计算时需要依靠第三方库进行解决,因此官方提出BinInt来解决问题。

null和undefined的区别

  • null表示为空,代表此变量没有值,一个对象可以是null,代表是空对象,而null本身不是对象,因为000开头代表是对象,而null全为零,所以将它误判为对象

  • undefined表示不存在,JS是一门动态类型语言,成员除了表示存在的值外,还有可能根本就不存在(因为是否存在只有在运行期间才知道),这就是undefined的意义所在。

类型转换的规则

转字符串

  • 任何数据和字符串相加都会先自动转换为String类型

    console.log('1'+NaN) // 1NaN
    console.log('1'+1) // 11
    console.log('1'+false, '1'+true) // 1false 1true
    console.log('1'+undefined) // 1undefined
    

转数字

  • 任何数据在做算数运算时(除了和字符串相加)或任何数据在和数字比较时,其它数据都会先自动转换为Number类型

  • 任何数据和NaN做算数运算(除了和字符串相加外),结果始终是NaN

    console.log(1-'1') // 0
    console.log(1+true) // 2
    console.log(1-'a') // NaN
    console.log(1+undefined) // NaN
    

转布尔

  • 在取反运算时,其它数据会自动转换为布尔值
  • 在分支结构或循环结构的条件判断中,其它数据会自动转换为布尔值

‘1’.toString()的调用

其实在这个语句运行的过程中做了以下几件事:

// 1\. 创建String类实例
var s = new String('1')
// 2\. 调用实例方法
s.toString()
// 3\. 执行完方法后立即销毁此实例
s = null

整个过程体现了基本包装类型的性质,而基本包装类型恰恰属于基本数据类型,包括Boolean、Number、String。

0.1+0.2 !== 0.3 ?

JS的Number类型遵循的是IEEE754标准,使用64位固定长度来表示。

0.1和0.2在转换成二进制后会进入无限循环,由于标准位数的限制,后面多余的位数会被截掉,此时就会出现精度丢失,相加后因浮点数小数位的限制而截断的二进制数字在转换为十进制就会出现精度丢失。

JS机制

变量提升

JS引擎的工作方式是,先解析代码,获取所有被声明的变量,然后再一行行的运行。这造成的结果,就是所有的变量的声明语句,都会被提升到代码的头部,这就叫做变量提升。

console.log(a) // undefined
var a = 1
function b() {
    console.log(a)
}
b() // 1

上面的代码实际的执行顺序是这样的: 第一步: 引擎将var a = 1拆解为var a = undefineda = 1,并将var a = undefined放在最顶端,a = 1还在原位置,这样一来代码就是:

var a = undefined; // 变量提升
console.log(a) // undefined
a = 1;
function b() {
    console.log(a)
}
b() // 1

第二步: 因此JS引擎一行行从上往下执行就造成了当前的结果,这就叫变量提升。

JS的作用域链

JS属于静态作用域,即声明的作用域是根据程序正文在编译时就确定的,有时也称为此法作用域。

其本质是JS在执行过程中会创造可执行上下文,可执行上下文的词法环境中含有外部词法环境的引用,我们可以通过此引用获取外部词法环境的变量、声明等,这些引用串联起来一直指向全局的词法环境,因此形成了作用域链。

箭头函数的this

箭头函数不同于传统JS中的函数,箭头函数并没有属于自己的this,它所谓的this是捕获其所在上下文的this值作为自己的this,箭头函数不会被new调用,这个所谓的this也不会被改变。

我们可以用Babel理解一下箭头函数:

// ES6
const obj = {
    getArrow() {
        return () => {
            console.log(this === obj)
        }
    }
}

转化后

// ES5,由Babel转译
var obj = {
    getArrow: function getArrow() {
        var _this = this;
        return function() {
            console.log(_this === obj)
        }
    }
}

作为一个开发者,有一个学习的氛围跟一个交流圈子特别重要,这是一个我的iOS开发交流群:130595548,不管你是大牛还是小白都欢迎入驻 ,让我们一起进步,共同发展!(群内会免费提供一些群主收藏的免费学习书籍资料以及整理好的几百道面试题和答案文档!)
iOS面试的一些小问题

上一篇:react实战之JSX


下一篇:Electron-vue实现后台多进程(三. 自动化测试篇)