1111
1.let, const和var的区别
2.JS的基础数据类型
3.什么是Symbol,她的使用场景
4.了解JS的作用域,函数作用域是什么时候形成的
5.如何自己实现一个new方法
6.说一下JS的继承方式
7.什么是箭头函数,它和普通函数的区别
8.this的指向
1.什么是盒模型
2.样式的优先级排序,如何去获取一个样式的相邻元素
3.position的属性有那些
4.列举一下移动端的适配方案
5.flex的布局了解吗,什么是主轴和副轴
6.less和css的区别,less函数
wepack中loader和plugin的区别
loader从字面的意思理解,是 加载 的意思。
由于webpack 本身只能打包commonjs规范的js文件,所以,针对css,图片等格式的文件没法打包,就需要引入第三方的模块进行打包。
loader虽然是扩展了 webpack ,但是它只专注于转化文件(transform)这一个领域,完成压缩,打包,语言翻译。
loader是运行在NodeJS中。
仅仅只是为了打包,仅仅只是为了打包,仅仅只是为了打包,重要的话说三遍!!!
如:css-loader和style-loader模块是为了打包css的
babel-loader和babel-core模块时为了把ES6的代码转成ES5
url-loader和file-loader是把图片进行打包的。
plugin也是为了扩展webpack的功能,但是 plugin 是作用于webpack本身上的。而且plugin不仅只局限在打包,资源的加载上,它的功能要更加丰富。从打包优化和压缩,到重新定义环境变量,功能强大到可以用来处理各种各样的任务。webpack提供了很多开箱即用的插件:CommonChunkPlugin主要用于提取第三方库和公共模块,避免首屏加载的bundle文件,或者按需加载的bundle文件体积过大,导致加载时间过长,是一把优化的利器。而在多页面应用中,更是能够为每个页面间的应用程序共享代码创建bundle。
plugins:[
//对html模板进行处理,生成对应的html,引入需要的资源模块
new HtmlWebpackPlugin({
template:'./index.html',//模板文件,即需要打包和拷贝到build目录下的html文件
filename:'index.html',//目标html文件
chunks:['useperson'],//对应加载的资源,即html文件需要引入的js模块
inject:true//资源加入到底部,把模块引入到html文件的底部
})
]
http 2.0 新特性:
二进制分帧
首部压缩
流量控制
多路复用
请求优先级
服务器推送
在用户输入 `URL`,按下回车之后,走过的步骤:
1. DNS 解析
2. TCP 连接
3. 发送 HTTP 请求
4. 服务器响应
5. 浏览器解析渲染页面
防抖
1. 设置定时器
2. 设置一个闭包,返回一个方法
3. 如果反复进来,清空前面的定时器,再重新设置一遍
```js
function debounce(fn) {
let timer = null;
return function() {
clearTimeout(timer);
timer = setTimeout(() => {
fn.call(this.arguments);
}, 1000);
}
}
```
节流
1. 设置一个标记
2. 设置一个闭包,返回一个方法
3. 如果重复进去的时候,标记已经动了,那就组织程序进一步运行
4. 如果定时器执行完了,设置这个标记为没动,允许下一次执行
```js
function throttle(fn) {
let timer = true;
return function() {
if (!timer) {
return;
}
timer = false;
setTimeout(() => {
fn.call(this, arguments);
timer = true;
}, 1000);
}
}
```
```js
const PENDING = 'pending';
const RESOLVED = 'resolved';
const REJECTED = 'rejected';
function myPromise(fn) {
const that = this;
that.status = PENDING;
that.value = null;
that.reason = null;
that.resolvedCallbacks = [];
that.rejectedCallbacks = [];
function resolve(value) {
if (that.status === PENDING) {
that.status = RESOLVED;
that.value = value;
that.resolvedCallbacks.map(cb => cb(value));
}
}
function reject(reason) {
if (that.status === PENDING) {
that.status = REJECTED;
that.reason = reason;
that.rejectedCallbacks.map(cb => cb(reason));
}
}
try {
fn(resolve, reject);
} catch(e) {
reject(e);
}
}
myPromise.prototype.then = function(onFullfilled, onRejected) {
const that = this;
if (that.status === PENDING) {
that.resolvedCallbacks.push(onFullfilled);
that.rejectedCallbacks.push(onRejected);
}
if (that.status === RESOLVED) {
onFullfilled(that.value);
}
if (that.status === REJECTED) {
onFullfilled(that.reason);
}
return that;
}
const p = new myPromise((resolve, reject) => {
setTimeout(() => {
resolve(1000);
}, 1000);
});
p.then((res) => {
console.log('结果:', res); // 结果:1000
}).then(() => {
console.log('jsliang'); // jsliang
})
```
\1. 自我介绍
\2. HTTP 请求头大致有哪些字段
\3. HTTPS
\4. Vue 双向数据绑定
\5. React Diff 比对详细
\6. React 等 MV* 优势
\7. 回流和重绘
\8. 预加载
\1. 行内元素和块级元素
\2. CSS 选择器
\3. 创建、添加、移除、复制 DOM 节点
\4. 数组
pop()、push()、shift()、unshift()的作用\5. 判断变量类型的方法、差异和局限
\6. 变量提升、函数提升、
Event Loop及var变量污染做打印题\7. 数组去重多种方式实现
\8. 千分位分割数字:
1234567890.11->1,234,567,890.11\9. 手写防抖和节流
\10. 实现柯里化
add(1)(2)(3)\11. 算法题:
前端面试中,常见的你可以回答的设计模式如下:
| 设计模式 | 描述 | 例子 |
| --- | --- | --- |
| 单例模式 | 一个类只能构造出一个唯一实例 | Redux/Vuex 的 Store |
| 工厂模式 | 对创建对象逻辑的封装 | jQuery 的 `$(selector)` |
| 观察者模式 | 当一个对象被修改时,会自动通知它的依赖对象 | Redux 的 `subsrcibe`、Vue 的双向绑定 |
| 装饰器模式 | 对类的包装,动态地扩展类的功能 | React 的高阶组件、ES7 装饰器 |
| 适配器模式 | 兼容新旧接口,对类的包装 | 封装旧 API |
| 代理模式 | 控制对象的访问 | 事件代理、ES6 的 `Proxy` |
\1. 自我介绍
\2. CSS 盒子模型
\3. CSS 样式优先级
\4. 垂直居中
\5. 移动端多分辨率适配
\6. 移动端
1px问题\7.
Cookie、SessionStorage、LocaleStorage\8. 登录态
\9.
Token\10. 实现动画效果(
setTimeout、requestAnimationFrame)\11. 手机按钮点击触发哪些事件并详细解释一下(
touchstart->touchmove->touchend->click)\12. 手机点击事件的监听
\13. 为什么
FastClick库中在touchend上进行自定义事件,而不是其他事件呢\14. 常见 HTTP 状态码,
204和304区别?301和302区别?\15. 浏览器同源策略,不同源会怎样?跨域会有什么限制?哪些操作要限制同源?
\16. 如何实现跨域请求
\17. 用
CORS进行跨域,简单请求和复杂请求有什么不同?\18. 说一下什么是防抖和节流,说一下它们的使用场景。
\19. 讲一下
Git Flow\20. 了解过排序算法吗?说一下冒泡排序和快速排序
* 防抖与节流
* 重绘与回流
* 浏览器解析 URL
* DNS 域名解析
* TCP 三次握手与四次挥手
* 浏览器渲染页面
* 虚拟 DOM 本质上是 JavaScript 对象,是对真实 DOM 的抽象
* 状态变更时,记录新树和旧树的差异
* 最后把差异更新到真正的 DOM 中
所以总结下来就是:
* 把树形结构按照层级分解,只比较同级元素。
* 给列表结构的每个单元添加唯一的 `key` 属性,方便比较。
* React 只会匹配相同 `class` 的 `component`(这里面的 `class` 指的是组件的名字)
* 合并操作,调用 `component` 的 `setState` 方法的时候, `React` 将其标记为 `dirty`。到每一个事件循环结束, React 检查所有标记 `dirty` 的 `component` 重新绘制.
* 选择性子树渲染。开发人员可以重写 `shouldComponentUpdate` 提高 `Diff` 的性能。
优点:
* **保证性能下限**:虚拟 DOM 可以经过 `Diff` 找出最小差异,然后批量进行 `patch`,这种操作虽然比不上手动优化,但是比起粗暴的 DOM 操作性能要好很多,因此虚拟 DOM 可以保证性能下限。
* **无需手动操作 DOM**:虚拟 DOM 的 `Diff` 和 `patch` 都是在一次更新中自动进行的,我们无需手动操作 DOM,极大提高开发效率。
* **跨平台**:虚拟 DOM 本质上是 JavaScript 对象,而 DOM 与平台强相关,相比之下虚拟 DOM 可以进行更方便地跨平台操作,例如服务器渲染、移动端开发等。
缺点:
* **无法进行极致优化**:在一些性能要求极高的应用中虚拟 DOM 无法进行针对性的极致优化,例如 VS Code 采用直接手动操作 DOM 的方式进行极端的性能优化。
Webpack 是一个现代 JavaScript 应用程序的静态模块打包器(`module bundler`)。
当 Webpack 处理应用程序时,它会递归地构建一个依赖关系图(`dependency graph`),其中包含应用程序需要的每个模块,然后将所有这些模块打包成一个或多个 `bundle`。
所以,它的本质是一个模块打包器,其工作是将每个模块打包成相应的 `bundle`。
* `mode`:模式。对应有开发模式、生产模式等
* `entry`:入口
* `output`:出口
* `loader`:模块转换器,用于把模块原内容按照需求转换成新内容。Webpack 对于 `.jpg`、`.txt` 等内容无法处理,就需要 `file-loader`、`url-loader` 等进行协助处理。
* `plugins`:扩展插件,在 Webpack 构建流程中的特定时机注入拓展逻辑来改变构建结果或者做其他你想做的事情。
Webpack 就像一条生产线,要经过一系列处理流程后才能将源文件转换成输出结果。
这条生产线上的每个处理流程的职责都是单一的,多个流程之间有存在依赖关系,只有完成当前处理后才能交给下一个流程去处理。
`Webpack` 的运行流程是一个串行的过程,从启动到结束会依次执行以下流程:
* **初始化参数**:从配置文件和 `Shell` 语句中读取与合并参数,得出最终的参数
* **开始编译**:用上一步得到的参数初始化 `Compiler` 对象,加载所有配置的插件,执行对象的 `run` 方法开始执行编译
* **确定入口**:根据配置中的 `entry` 找出所有的入口文件
* **编译模块**:从入口文件出发,调用所有配置的 `Loader` 对模块进行翻译,再找出该模块依赖的模块,再递归本步骤直到所有入口依赖的文件都经过了本步骤的处理
* **完成模块编译**:在经过第 4 步使用 `Loader` 翻译完所有模块后,得到了每个模块被翻译后的最终内容以及它们之间的依赖关系
* **输出资源**:根据入口和模块之间的依赖关系,组装成一个个包含多个模块的 `Chunk`,再把每个 `Chunk` 转换成一个单独的文件加入到输出列表,这步是可以修改输出内容的最后机会
* **输出完成**:在确定好输出内容后,根据配置确定输出的路径和文件名,把文件内容写入到文件系统
简单来说:
* **初始化**:启动构建,读取与合并配置参数,加载 `Plugin`,实例化 `Compiler`(钩子)
* **编译**:从 `Entry` 出发,针对每个 `Module`(模块)串行调用对应的 `Loader` 去翻译文件的内容,再找到该 `Module` 依赖的 `Module`,递归地进行编译处理
* **输出**:将编译后的 `Module` 组合成 `Chunk`,将 `Chunk` 转换成文件,输出到文件系统中(`Chunk` 就是打包过程中,入口模块引用其他模块,模块再引用模块,这个关系链连接的 `Module` 就形成了 `Chunk`)
在这个过程中,`Webpack` 会在特定的时间点广播出特定的事件,插件在监听到感兴趣的事件后会执行特定的逻辑,并且插件可以调用 `Webpack` 提供的 `API` 改变 `Webpack` 的运行结果。
* [x] 前端历史演进
* [x] React 和原生对比
* [x] 组件化
* [x] 天然分层
* [x] 生态好
* [x] 开发效率
* [x] React 和 Vue 对比
* [x] 相同之处:虚拟 DOM、组件化、构建工具、配套框架、Chrome 开发工具
* [x] 不同之处:模板 和 JSX、监听数据变化方式不同、Diff 不同
* [x] React Fiber
* [x] React 生命周期
* [x] 挂载阶段:`constructor`、`getDerivedStateFromProps`、`render`、`componentDidMount`
* [x] 更新阶段:`getDerivedStateFromProps`、`shouldComponentUpdate`、`render`、`getSnapshotBeforeUpdate`、`componentDidUpdate`
* [x] 卸载阶段:`componentWillUnmount`
* [x] `setState`
* [x] 调用之后发生什么
* [x] 同步还是异步
* [x] this 指向问题
* [x] 通过 `bind` 修正
* [x] 通过箭头函数修正
* [x] `bind` 和箭头函数的区别
* [x] 受控组件和非受控组件:`value` 和 `defaultValue`
* [x] 组件通讯
* [x] `props`
* [x] `Context`
* [x] `Redux`
* [x] `Redux`:`Redux`、`React-Redux` 以及 `Redux-Saga` 工作流
* [x] `Mixin`、`HOC` 和 `Hook`
* [x] 性能优化
* [x] 首屏渲染优化 `prerender-spa-plugin`
* [x] 页面占位 `react-placeholder`
* [x] 页面切换优化 `html-webpack-plugin`
* [x] 减少业务体积代码 `Tree Shaking`
* [x] 提取公共代码 `SplitChunkPlugin`
* [x] 切分代码 `Code Splitting`
* [x] 懒加载 `react-lazyload`
* 优点
1、响应式
Vue 最大的优点,通过 MVVM 思想实现数据的双向绑定,通过虚拟 DOM 让数据操作 DOM,而不必操作真实 DOM,提升性能,让开发者有更多时间去思考业务逻辑。
2、组件化
把一个单页应用中的各个模块拆分到一个个组件当中,或者把一些公用的部分抽取出来做成一个可复用的组件。提高开发效率,方便重复使用,使项目的可维护性更强。
* 缺点
1、不利于 SEO。
2、导航不可用,如果一定要导航需要自行实现前进、后退。(由于单页面不能用浏览器的前进后退功能,所以需要自己建立堆栈管理)。
3、初次加载时耗较多。
1. 明显区别。`hash` 模式的 URL 夹杂 `#` 号,而 `history` 没有
2. 底层不同。`hash` 模式依靠 `onhashchange` 事件,监听 `location.hash` 的改变;而 `history` 模式主要是依靠 HTML5 history 新增的两个方法:`pushState()` 和 `repalceState()`,其中 `pushState()` 可以改变 `url` 地址且不会发送请求,`repalceState()` 可以读取历史记录栈,对浏览器记录进行修改。
1. 查询 `www.baidu.com`
2. 访问客户端 DNS 缓存:**浏览器缓存** -> **系统缓存(host)** -> **路由器缓存**
3. 访问 **ISP DNS 服务器**(ISP,互联网服务提供商,有联通电信移动等。如果你是电信网络,则进入电信的 DNS 缓存服务器,以下简称本地),如果本地服务器有,则直接返回;如果没有,让本地 DNS 服务器去逐个咨询查找。
4. 本地去咨询 **DNS 根服务器**,DNS 根服务器发现是 `.com 区域` 管理的,告诉本地去咨询它。
5. 本地去咨询 **.com 顶级域服务器**,.com 域服务器告诉本地去咨询 `baidu.com 主区域` 的服务器。
6. 本地去咨询 **baidu.com 主域名服务器**,baidu.com 域服务器查找到对应的 IP 地址,返回给本地。
7. 本地 DNS 云服务器通知用户对方 IP 地址,同时缓存这个 IP 地址,下次就直接访问了。
这个阶段是通过 “三次握手” 来建立客户端和服务器之间的连接。
`TCP` 提供面向连接的通讯传输。面向连接是指数据通讯开始先做好两端之间的准备工作。
所谓 **三次握手**,是指在建立一个 `TCP` 连接时,客户端和服务器总共要发送 3 个数据包来确认连接的建立。
三次握手主要作用是:
* 确认双方的接收能力和发送能力
* 指定自己的初始化序列号,为后面的可靠性做准备
刚开始客户端处于 `Closed` 的状态,服务器处于 `Listen` 状态。
1. **客户端发送到服务器**。客户端发送 `SYN` 报文给服务器,并且指明客户端初始化序列号为 `ISN(c)`,即以 `SYN=1, seq=x` 的形式发送过去。此时客户端处于 `SYN_SEND` 状态。
2. **服务器发送给客户端**。服务器收到客户端的 `SYN` 和 `ISN(c)`,也发送一个 `SYN` 回去,同时设置 `ACK = ISN(c) + 1` 以及指明服务器初始化序列号为 `ISN(s)`,即以 `SYN=1, ACK=x+1, seq=y` 的形式发送给客户端。
3. **客户端发送到服务器**。客户端收到服务器发送的消息后,设置 `ACK = ISN(s) + 1`,将自身的 `ISN(c) + 1`,即以 `ACK=y+1, seq=x+1` 的形式发送给服务器。此时客户端处于 `ESTABLISHED` 阶段,服务器收到报文,也处于 `ESTABLISHED` 阶段,双方建立了连接。
第一次握手:客户端发送syn包(seq=x)到服务器,并进入SYN_SEND状态,等待服务器确认;第二次握手:服务器收到syn包,必须确认客户的SYN(ack=x+1),同时自己也发送一个SYN包(seq=y),即SYN+ACK包,此时服务器进入SYN_RECV状态;第三次握手:客户端收到服务器的SYN+ACK包,向服务器发送确认包ACK(ack=y+1),此包发送完毕,客户端和服务器进入ESTABLISHED状态,完成三次握手。
* **客户端发送给服务器**。客户端以 `FIN=1, seq=u` 的形式发送给服务器,表示需要关闭客户端和服务器的数据传输。此时客户端处于 `FIN_WAIT` 状态。
* **服务器发送给客户端**。服务器收到信息,先返回 `ACK` 给客户端,即以 `ACK=1, seq=v, ack=u+1` 的形式返回给客户端,表示收到客户端报文了。此时服务器处于 `CLOST_WAIT` 状态。
* **服务器发送给客户端**。服务器等待一会,看客户端还有没有数据没发过来,等处理完这些数据之后,也想断开连接了,于是发送 `FIN` 给客户端,即以 `FIN=1, ACK=1, seq=w, ack=u+1` 的形式发送给客户端。此时服务器处于 `LAST_ACK` 状态。
* **客户端发送给服务器**。客户端收到 `FIN` 之后,返回 `ACK` 报文作为应答,即以 `ACK=1, seq=w+1` 的形式发送给服务器。此时客户端处于 `TIME_WAIT` 状态。
第一次挥手:主动关闭方发送一个FIN,用来关闭主动方到被动关闭方的数据传送,也就是主动关闭方告诉被动关闭方:我已经不会再给你发数据了(当 然,在fin包之前发送出去的数据,如果没有收到对应的ack确认报文,主动关闭方依然会重发这些数据),但此时主动关闭方还可以接受数据。
第二次挥手:被动关闭方收到FIN包后,发送一个ACK给对方,确认序号为收到序号+1(与SYN相同,一个FIN占用一个序号)。
第三次挥手:被动关闭方发送一个FIN,用来关闭被动关闭方到主动关闭方的数据传送,也就是告诉主动关闭方,我的数据也发送完了,不会再给你发数据了。
第四次挥手:主动关闭方收到FIN后,发送一个ACK给被动关闭方,确认序号为收到序号+1,至此,完成四次挥手。
最明显的区别就是在模块定义时对依赖的处理不同
1、AMD推崇依赖前置,在定义模块的时候就要声明其依赖的模块
2、CMD推崇就近依赖,只有在用到某个模块的时候再去require
beforeCreate :数据还没有挂载呢,只是一个空壳
created:这个时候已经可以使用到数据,也可以更改数据,在这里更改数据不会触发updated函数
beforeMount:虚拟dom已经创建完成,马上就要渲染,在这里也可以更改数据,不会触发updated
mounted:此时,组件已经出现在页面中,数据、真实dom都已经处理好了,事件都已经挂载好了
beforeUpdate:重新渲染之前触发,然后vue的虚拟dom机制会重新构建虚拟dom与上一次的虚拟dom树利用diff算法进行对比之后重新渲染
updated:数据已经更改完成,dom也重新render完成
beforeDestory:销毁前执行($destroy方法被调用的时候就会执行),一般在这里善后:清除计时器、清除非指令绑定的事件等等...
destroyed:组件的数据绑定、监听...都去掉了,只剩下dom空壳,这里也可以善后
React 生命周期分为三种状态 1. 初始化 2.更新 3.销毁(其实vue差不多啊)
// 组件初始化的时候触发的函数:
// constructor 、componentWillMount、 render 、componentDidMount
@1 react初始化的顺序是,constructor然后是componentWillMount(相当于vue 的beforeMounted,) 然后是 render渲染函数再然后是componentDidMount(相当于vue的mounted )
@2 更新的时候:componentWillUpdate、render、componentDidUpdate(跟vue一样 平时没在用到更新的钩子函数)
比如也没一个按钮,点击改变msg
输入一个数字,将数字按照用逗号分隔,例如输入1234.56,结果是1,234.56
/**
* 输入数组,返回按照位数分割的数组
* @param number 传入的数字
* @param range 按照此位数将数字分割
* @returns {string} 返回用逗号分隔的字符串
*/
const splitStr = (number, range = 3) => {
let str = number.toString();
const[left, right] = str.split('.');
const strArr = left.split('').reverse();
let result = [];
for (let i = 0; i < strArr.length; i += range) {
result.push(strArr.slice(i, i + range).reverse().join(''))
}
return `${result.reverse().join(',')}.${right}`
};
const a = 12345.11;
console.log(splitStr(a)); // 123,45.11
// 内置的方法
Number.toLocalString();
const a = 12345.11;
console.log(a.toLocalString()); // 123,45.11
数字、金额进行千分位分割
var num = 123456789;
num = num + '';
console.log(num.replace(/(?=(?!^)(?:\d{3})+(?:\.|$))(\d{3}(\.\d+$)?)/g,',$1'));
1.jsonp
2. cors
3. Node 正向代理 利⽤服务端不跨域的特性
4. nginx 反向代理 proxy_pass
5. img标签
原型链
继承的实现
数据类型
var、const、let 对比
new 的过程
this 指向问题
bind 实现方式
闭包
事件循环
类型判断
手写 Promise
这里罗列几个 vue 问题:
vue 生命周期,每个生命周期项目中什么时候会用
vue keep-alive 常用属性
组件通信方式
hash 和 history 区别
v-for 唯一 key
vue 运行机制,依赖收集
v-show 和 v-if
watch 和 computed
vuex 中模块拆怎么做的
涉及到的编码题:
数组去重
css 水平垂直居中
一个页面有父子组件,进入之后的渲染顺序触发的生命周期是什么样的
keep-alive,如果只想要router-view里面的某个组件被缓存,怎么做
组件通信中的eventbus原理是什么
vue diff简单讲讲
3.0proxy 讲讲,和 2.0 区别(广度)
data为什么是函数
常用vue api原理(包括不限于nextTick,watch,computed)
涉及到的编码题:
call,apply,bind
节流,防抖
class实现
这里给点例子:
promise.all 异常处理
版本号比较排序
数组中第 k 个最大元素
二叉树中的所有路径
二叉树中和为某一值的路径
node 中 promisify 实现
fetch 兼容超时重传
观察者模式(高频)
String indexOf 实现
扁平化
科里化
1.减少 css,js 文件数量及大小(减少重复性代码,代码重复利用),压缩 CSS 和 Js 代码 2.图片的大小 3.把 css 样式表放置顶部,把 js 放置页面底部 4.减少 http 请求数 5.使用外部 Js 和 CSS
全局作用域:
全局作用域贯穿整个javascript文档,在所有函数声明或者大括号之外定义的变量,都在全局作用域里。一旦你声明了一个全局变量,那么你在任何地方都可以使用它,包括函数内部。事实上,JavaScript默认拥有一个全局对象window,声明一个全局变量,就是为window对象的同名属性赋值。如下面代码所示。 注:若变量在函数内部没有声明(未使用var关键字),该变量为全局变量。全局变量在页面关闭后销毁。
局部作用域(函数作用域和块级作用域)
在JavaScript中,任何定义在函数体内的变量或者函数都将处于函数作用域中,这些变量也无法被在函数外部使用。(当然使用闭包也可以访问)
变量在函数内声明,变量为局部作用域。
局部变量:只能在函数内部访问。
局部变量只作用于函数内,所以不同的函数可以使用相同名称的变量。
局部变量在函数开始执行时创建,函数执行完后局部变量会自动销毁。
作用域链:
遍历嵌套作用域链的规则很简单:引擎从当前的执行作用域开始查找变量,如果找不到, 就向上一级继续查找。当抵达最外层的全局作用域时,无论找到还是没找到,查找过程都会停止。
简单来讲,局部作用域(如函数作用域)可以访问到全局作用域中的变量和方法,而全局作用域不能访问局部作用域的变量和方法。