til
  • README
  • Software Development Roles
  • solid
  • README
    • service-worker
  • docker
    • arg
    • 更新docker版本
  • editor
    • vscode
    • Creating a VS Code Theme
  • english
    • words
  • front-end
    • ==
    • ECMAScript
    • IIFE
    • Label
    • basic
    • html.js.css渲染顺序
    • npm-vs-yarn
    • obj-delete-key-value
    • react
    • split-join-and-replace
    • video
    • 前端自检清单
    • 递归及去重
    • css
      • css换肤
      • flex
      • list
      • nth-child和nth-of-type区别
      • padding
      • position
      • 层叠上下文
      • 层叠样式(+)
      • 正方形
      • 语义化标签
    • dom
      • DOCTYPE
      • HEAD
      • 修改document
      • 自定义表单验证
    • electron
      • basic
    • es6
      • basic-type
      • basic
      • prototype-example
      • defineProperty
      • understanding-es6
        • 0.introduction
        • Appendix A: Smaller Changes
        • Appendix B: Understanding ES7
        • Block-Binding
        • Proxies&Reflection
        • class
        • 解构赋值
        • function
        • improved-array
        • iterators&generators
        • modules
        • object
        • promise
        • Map&Set
        • symbol
    • images
      • 前端角度看图片
    • interview_case
      • lexical_scope
      • redux和localstroage存储位置
    • javascript
      • fuck-the-js
      • js-engine-work
      • js原生操作dom
      • what-is-function-program
      • 执行上下文
      • articles
        • JavaScript中使用函数组合
        • JavaScript中的依赖注入
        • JavaScript作用域链中的标识符解析和闭包
        • JavaScript是何如工作的--概述
        • JavaScript深拷贝
        • JavaScript的全局变量是如何工作的
        • js继承常见的误解
        • node12&chrome中7个新的提案功能
        • 你真的懂JavaScript吗
      • date
        • index
      • engines
        • basic
        • JavaScript引擎基础:外形和内联缓存
        • v8中推测性优化的介绍
        • 优化原型
        • 更快的异步功能和promise
      • events
        • baisc
        • 事件冒泡和捕获
        • 定义事件
        • 页面生命周期
      • higher-order-function
        • curry
        • monad
      • module
        • basic
        • main&module
      • objects
        • iterator
        • spread
        • examples
          • iterator
      • performance
        • blocking-css
        • cache
      • prototype
        • Property-Descriptors
        • basic
        • prototype-shadow
      • you-dont-known-js
        • async&performance
          • Chapter 1: Asynchrony: Now & Later
          • Chapter 2: Callbacks
          • Chapter 3: Promises
          • Chapter 4: Generators
        • scope & closures
          • apA
          • apB
          • apC
          • apD
          • chapter1-what-is-scope
          • chapter2-lexical-scope
          • chapter3-function-vs-block-scope
          • chapter4-hoisting
          • chapter5-scope-closure
        • this & object prototypes
          • chapter1-this-or-that
          • chapter2-this-make-sense
          • chapter3-objects
          • chapter4-mixing(up)-class-object
          • chapter5-prototype
          • chapter6-behavior-delegation
        • types&grammer
          • Chapter1-Types
          • Chapter2-Values
          • Chapter3-Natives
          • Chapter4-coercion
          • Chapter5-grammer
        • up & going
          • chapter1-into-programming
          • chapter2-into-javascript
          • chapter3-into-YDKJS
    • mobile
      • iPhone分辨率终极指南
    • npm
      • arguments
      • build
    • react-native
      • prop-methods
    • react
      • PropTypes
      • basic
      • codebase-overview
      • component-element-instance
      • context
      • how-to-known-component-is-func-or-class
      • overview
      • react16.9
      • react18计划
      • react的设计原则
      • reconciliation
      • setState
      • useMemo
      • why-do-we-write-super-props
      • 从头实现一个react
      • concurrent
        • 引入并发模式(仅试验)
      • conf
        • conf-2019
      • events
        • 合成事件概述
      • hooks
        • custom-hook
        • effect-hook
        • hooks-api
        • intro
        • overview
        • rules
        • state-hook
        • hooks-vs-class
          • thinking-in-react-hooks
      • overreact
        • Development模式是如何工作的
        • How-Does-setState-Know-What-to-Do
        • Why-Do-React-Elements-Have-a-$$typeof-Property
        • Why-Do-React-Hooks-Rely-on-Call-Order
        • how-to-known-component-is-func-or-class
        • preparing-tach-talk-motivation
        • react作为ui运行
        • things-i-dont-known-as-2018
        • ui-element-problem-and-build-yourself
        • why-do-we-write-super-props
        • 一份完整的useEffect指南
        • 为什么X不是Hook
        • 函数组件与类有什么不同?
        • 演讲准备2-what-why-how
        • 编写弹性组件
        • 让setInterval在React-Hooks中为声明式
      • practice
        • render
      • react-dom
        • basic
      • react-redux
        • apiv7.1-hooks
        • connect
        • shallow-equal
      • redux
        • applyMiddleware
        • applyMiddleware2-细节
        • example
    • regex
      • index
    • stories
      • 数组下标
      • 阻止事件冒泡
    • svelte
      • compile-svelte-in-your-head-1
      • compiler-overview
      • parser
        • 写一个解析器-JavaScript的JSON解析器
    • turbopack
      • basic
    • typescript
      • interface和type的区别
    • webpack
      • hash
      • webpack4-for-react
      • webpack4
      • webpack4to5
      • babel
        • babel-parser和acorn的区别
        • babel.7.11
        • family
        • react16.14使用new-transform
        • update-to-7
    • pdf
      • deep-js
        • basic
      • react
        • reintroducing
  • git
    • capital
    • emoji
  • http
    • http2.0
    • response
  • rails
    • api
    • flash
    • middleware-vs-metal
    • model
    • performance
    • routes
    • environment
      • error
    • patterns
      • service
    • sidekiq
      • params
    • deploy
      • capistrano
        • ssh
  • ruby
    • self
    • net
      • http请求携带cookie
  • server
    • ss
    • ssh
    • user
    • crawler
      • puppeteer
    • nginx
      • domain-without-80
      • nginx节省带宽
  • sql
    • rails
    • search
Powered by GitBook
On this page
  • 解答
  • 步骤
  1. front-end
  2. javascript
  3. articles

你真的懂JavaScript吗

Previousnode12&chrome中7个新的提案功能Nextdate

Last updated 5 years ago

放在前面,本文原文的标题是 So you think you know JavaScript?

在下感觉有些标题党了,不过看了下文章的链接还是很不错的。

原文作者是由几个问题展开了说明。

问题 1: 浏览器的console里会打印出什么?

var a = 10;
function foo() {
    console.log(a); // ??
    var a = 20;
}
foo();

问题2: 如果是有const或let代替var,输出是否一样?

var a = 10;
function foo() {
    console.log(a); // ??
    let a = 20;
}
foo();

问题3: "newArray"中的元素是什么?

var array = [];
for(var i = 0; i <3; i++) {
 array.push(() => i);
}
var newArray = array.map(el => el());
console.log(newArray); // ??

问题4:如果我们在浏览器控制台中运行'foo'函数,是否会导致堆栈溢出错误?

function foo() {
  setTimeout(foo, 0); // will there by any stack overflow error?
};

问题5:如果我们在控制台中运行以下函数,页面的UI(tab页)是否仍然响应?

function foo() {
  return Promise.resolve().then(foo);
};

问题6:我们可以在不引起TypeError的情况下以某种方式使用以下语句的扩展语法吗?

var obj = { x: 1, y: 2, z: 3 };
[...obj]; // TypeError

问题7:运行以下代码片段时,控制台上会打印什么?

var obj = { a: 1, b: 2 };
Object.setPrototypeOf(obj, {c: 3});
Object.defineProperty(obj, 'd', { value: 4, enumerable: false });

// what properties will be printed when we run the for-in loop?
for(let prop in obj) {
    console.log(prop);
}

问题8:xGetter()将输出什么值?

var x = 10;
var foo = {
  x: 90,
  getX: function() {
    return this.x;
  }
};
foo.getX(); // prints 90
var xGetter = foo.getX;
xGetter(); // prints ??

解答

现在,让我们从头到尾回答上面的每个问题。我将给一个简短的解释,同时试图揭开这些行为的神秘面纱,并提供一些参考资料。

答案 1: undefined

var a = 10; // 全局作用域
function foo() {
// 使用var声明的会被提升到函数作用域内顶部.
// 就像: var a;

console.log(a); // 打印 undefined

// 实际初始化值20只发生在这里
   var a = 20; // 本地 scope
}

答案 2: ReferenceError: a is not defined

var a = 10; // 全局作用域
function foo() { // 进入新的作用域, TDZ开始

// 没有初始绑定的'a'被创建
    console.log(a); // ReferenceError

// TDZ 结束, 'a'只是在这里被初始化了一个值20
    let a = 20;
}

答案 3: [3, 3, 3].

// 误解作用域:认为存在块级作用域
var array = [];
for (var i = 0; i < 3; i++) {
  // 三个箭头函数中的每个都引用同一个绑定,
  // 这就是为什么循环结束之后返回同样的数字3
  array.push(() => i);
}
var newArray = array.map(el => el());
console.log(newArray); // [3, 3, 3]

如果你声明一个具有块级作用域的变量,则会为每个循环迭代创建一个新绑定。

// 使用ES6块级作用域绑定
var array = [];
for (let i = 0; i < 3; i++) {
  // 这一次,每个“i”引用一个特定迭代的绑定,并保留当时的值。
  // 因此,每个arrow函数返回一个不同的值。
  array.push(() => i);
}
var newArray = array.map(el => el());
console.log(newArray); // [0, 1, 2]

解决这个问题的另一种方法是使用闭包。

let array = [];
for (var i = 0; i < 3; i++) {
  array[i] = (function(x) {
    return function() {
      return x;
    };
  })(i);
}
const newArray = array.map(el => el());
console.log(newArray); // [0, 1, 2]

答案 4: 不会

解释: JavaScript并发模型基于“事件循环”。当我说“浏览器是JS的家(归宿)”时,我真正的意思是浏览器提供运行时环境来执行我们的JavaScript代码。浏览器的主要组件包括 调用堆栈 ,事件循环 ,任务队列 和 Web API 。像setTimeout,setInterval和Promise这样的全局函数不是JavaScript的一部分,而是Web API的一部分。JavaScript环境的可视化表示如下所示:

JS调用堆栈是后进先出(LIFO)。引擎一次从堆栈中获取一个函数,并从上到下依次运行代码。每次遇到一些异步代码(如setTimeout)时,它都会将其交给Web API(箭头1)。因此,每当触发事件时,callback都会被发送到任务队列(箭头2)。Event Loop不断监视任务队列,并按照排队顺序一次处理一个callback。每当调用堆栈为空时,循环检索回调并将其放入堆栈(箭头3)进行处理。请记住,如果调用堆栈不为空,则事件循环不会将任何callbacks推送到堆栈。

现在,有了这些知识,让我们试着回答上述问题:

步骤

  1. 调用foo()将把foo函数放进调用栈。

  2. 在处理内部代码时,JS引擎遇到setTimeout。

  3. 然后它将foo回调移交给 WebAPI (箭头1)并从函数返回。调用堆栈再次为空。

  4. 计时器设置为0,因此foo将被发送到 任务队列 (箭头2)。

  5. 因为,我们的调用堆栈是空的,事件循环将选择foo回调并将其推送到调用堆栈进行处理。

  6. 进程再次重复,堆栈不会溢出 。

笔:其实这个答案里的链接和下面答案的链接很给力了。

答案 5: 不会

解释: 大多数时候,我看到开发人员假设在事件循环的蓝图中只有一个任务队列(笔: 也叫task queue或event queue或callback queue )。但事实并非如此。我们可以有多个任务队列。由浏览器选择任意的队列并在其中处理callbacks。

在执行返回事件循环之前,微任务队列总是被清空

现在,当你在控制台中运行以下代码段时:

function foo() {
  return Promise.resolve().then(foo);
};

每次调用'foo'都会继续在微任务队列上添加另一个'foo'回调,因此事件循环无法继续处理其他事件(scroll,click等),直到该队列完全清空为止。因此,它会阻止渲染。

笔:Jake的此文绝对是精华,没有读过的可以拜读一番。

上述陈述可能看起来有点冗长,但下面的例子会更有意义:

var obj = { x: 1, y: 2, z: 3 };
obj[Symbol.iterator] = function() {

  return {
    next: function() {
      if (this._countDown === 3) {
        const lastValue = this._countDown;
        return { value: this._countDown, done: true };
      }
      this._countDown = this._countDown + 1;
      return { value: this._countDown, done: false };
    },
    _countDown: 0
  };
};
[...obj]; // will print [1, 2, 3]
var obj = { x: 1, y: 2, z: 3 };
obj[Symbol.iterator] = function*() {
  yield 1;
  yield 2;
  yield 3;
};
[...obj]; // print [1, 2, 3]

笔:对这个不熟悉的可以看下一些例子:

答案 7: a, b, c

var obj = { a: 1, b: 2 };
var descriptor = Object.getOwnPropertyDescriptor(obj, "a");
console.log(descriptor.enumerable); // true
console.log(descriptor);
// { value: 1, writable: true, enumerable: true, configurable: true }

现在掌握了这些知识,应该很容易理解为什么我们的代码会打印出这些特定的属性:

var obj = { a: 1, b: 2 }; // a, b are both enumerables properties


Object.setPrototypeOf(obj, { c: 3 });

Object.defineProperty(obj, "d", { value: 4, enumerable: false });

for (let prop in obj) {
  console.log(prop);
}

笔:对这个不了解的可以看文章了解一下

答案 8: 10

var x = 10; // 全局作用域
var foo = {
  x: 90,
  getX: function() {
    return this.x;
  }
};
foo.getX(); // prints 90
let xGetter = foo.getX;
xGetter(); // prints 10

我们可以断言:

window.x === 10; // true

this 将始终指向调用该方法的对象。因此,在foo.getX() 的情况下,this 指向foo对象返回值90。而在xGetter()的情况下,this 指向window 对象返回值10。

let getFooX = foo.getX.bind(foo);
getFooX(); // prints 90

笔:这个说的主要就是this了,不了解的可以看下

就是这样!如果你所有的答案都正确,那就做得好。错了不可怕,因为我们都从错误中学习。关键是要知道背后的“原因”。

你都对了吗老兄。

解释: 使用var关键字声明的变量被并在内存中为其赋值为undefined。但是初始化恰好发生在你在代码中写入它们的地方。另外,var声明的变量是的,而let和const是块作用域的。所以,这就是这个过程的样子:

笔:对这个不了解的,可以看下。

解释: let和const允许你声明一个变量被限制在一个块级作用域,或语句或表达式中。不像var,这些变量不会被提升,并且具有所谓的(TDZ)。尝试在TDZ中访问这些变量将抛出一个ReferenceError,因为它们只能在执行到达声明才可被访问。可以阅读和。

下表概述了与JavaScript中使用的不同关键字相关的提升行为和范围(主要摘选:的博客)。

image-20190810210348073

解释: 在for loop的头部声明一个带有var关键字的变量,为该变量创建一个绑定(存储空间)。阅读关于的更多信息。让我们再看一次for循环。

为啥let可以,可以参考

alt text

有关Event Loop如何在JavaScript中工作的更详细说明,我强烈建议您观看Philip Roberts的。此外,你还可以通过这个非常棒的可视化和理解调用堆栈。来吧,在那里运行'foo'函数,看看会发生什么!

不过也可以看看

在高层次上来看,JavaScript中有宏任务和微任务。setTimeout回调是 macrotasks 而Promise回调是 microtasks 。主要的区别在于他们的执行仪式。宏任务在单个循环周期中一次一个地推入堆栈,但是微任务队列总是在执行返回到event loop(包括任何额外排队的项)之前清空。因此,如果你将这些项快速的添加到这个你正在处理的队列,那么你将永远在处理微任务。有关更深入的解释,请观看的或。

答案 6: 可以, 通过是对象

解释: 和语句迭代iterable对象。数组或Map是具有默认迭代行为的内置iterable。对象不是可迭代的,但你可以使用和协议使它们可迭代。

在Mozilla文档中,如果一个对象实现了@@iterator方法,那么它就是可迭代的,这意味着这个对象(或者它上的一个对象)必须有一个带有@@iterator键的属性,这个键可以通过常量Symbol.iterator获得。

你还可以使用函数来自定义对象的迭代行为:

解释: for-in循环遍历对象本身的以及对象从其原型继承的属性。可枚举属性是可以在for-in循环期间包含和访问的属性。

解释: 当我们将x初始化为全局作用域时,它将成为window对象的属性(假设它是浏览器环境而不是模式)。看下面代码:

要检索foo.x的值,我们可以通过使用将 this 的值绑定到foo对象来创建新函数。

原文:

提升
函数作用域
这篇文章了解一番
temporal dead zone
词法作用域
执行上下文栈
Axel Rauschmayer
文章
闭包
这篇文章
视频
工具
其他的
Jake Archibald
视频
文章
iterables
拓展运算符
for-of
iterable
iterator
原型链
generator
iterator&generator
可枚举属性
property descriptors
object getPrototypeOf&setPrototypeOf
严格
Function.prototype.bind
this All Makes Sense Now
So you think you know JavaScript?