一 目录

不折腾的前端,和咸鱼有什么区别

目录
一 目录
二 var/let/const
三 暂时性死区
四 函数作用域和块级作用域
五 变量晋升和函数晋升
 5.1 具体解说:变量晋升、函数晋升和暂时性死区
 5.2 题目 1
 5.3 题目 2
 5.4 题目 3
 5.5 题目 4
 5.6 题目 5
六 判断变量
 6.1 typeof
 6.2 instanceof
 6.3 constructor
 6.4 Array.prototype.toString.call
七 参考文献

二 var/let/const

返回目录
  • var
  1. var 能够反复申明
  2. 作用域:全局作用域 和 函数作用域
  3. 会进行预解析
  • let
  1. 对立作用域下不能反复申明
  2. 作用域:全局作用域 和 块级作用域 {}
  3. 不进行预解析
  • letvar 比拟:
  1. var 申明的变量只能是全局或者整个函数块的
  2. let 容许申明一个在作用域限度在块级的变量、语句或者表达式(块级作用域)
  3. let 不能反复申明
  4. let 存在长期死区(temporal dead zone)
  5. let` 不会被预解析(hoisting)
  • const
  1. let 有的它也有
  2. 初始化必须赋值
  3. 赋值后不能改变类型

三 暂时性死区

返回目录

只有块级作用域内存在 let/const 命令,它所申明的变量就绑定这个区域,不再受内部的影响。

在代码块内,应用 let/const 申明变量之前,该变量都是不可用的,所以叫 “暂时性死区”。

看一道题目:

上面代码输入什么?

let a = 1;let test = function() {  console.log(a);  a++;}test();

上面代码输入什么?

let a = 1;let test = function() {  console.log(a);  let a = 2;  a++;}test();

第一道题输入:1

第二道题输入:Uncaught ReferenceError: Cannot access 'a' before initialization

其起因是在同一个 block 中,let 在前面从新定义的,那么就不能在之前援用该变量。同时,也不能取嵌套外层的值。

四 函数作用域和块级作用域

返回目录
  • 函数作用域:在 JavaScript 中定义一个函数,函数外部的变量只能通过函数外部拜访,同时它能够批改和影响内部变量。
  • 块级作用域:变量在来到定义的块级代码后立刻被回收,存在暂时性死区的个性。

在 ES5 中只有全局作用域和函数作用域,没有块级作用域,这会带来些许麻烦:

  1. 内层变量笼罩外层变量
  2. 循环的变量泄露为全局变量
内层变量笼罩外层变量
var tmp = new Date();function f(){  console.log(tmp);  if(false){    var tmp = "hello";  }}f(); // undefined
循环的变量泄露为全局变量
for (var i = 0; i < 3; i++) {}console.log(i); // 3

通过 var 申明的变量或者非严格模式下(non-strict mode)创立的函数申明没有块级作用域。

var x = 1;{  var x = 2;}console.log(x); // 2

所以,为了解决这个问题,ES6 新增了块级作用域:

  • 块语句用于组合零个或者多个语句,这个块由一对大括号 {} 界定。
ES5 造成块级作用域的办法
(function() {})()
ES6 通过 {} 界定
{  var x = 1; // 或者 const x = 1;}

五 变量晋升和函数晋升

返回目录

对于为什么进行变量晋升和函数晋升,这个问题始终没有明确的答案。

函数晋升就是为了解决互相递归的问题,大体上能够解决自下而上的程序问题。

// 验证偶数function isEven(n) {  if (n === 0) {    return true;  }  return isOdd(n - 1);}console.log(isEven(2)); // true// 验证奇数function isOdd(n) {  if (n === 0) {    return false;  }  return isEven(n - 1);}

如果没有函数晋升,而是依照自下而上的程序,当 isEven 函数被调用时,isOdd 函数还未声明,所以在 isEven 外部无奈调用 isOdd 函数。

而变量晋升的缘故,这里就不太分明了,答复下来就是【设计如此】。

5.1 具体解说:变量晋升、函数晋升和暂时性死区

返回目录

那么,变量晋升是怎么的呢?

console.log(a);var a = 10;

在这段脚本中,会输入 undefined,为什么?

var a;console.log(10);a = 10;

看下面代码,在 JavaScript 解析的过程中,会将 a 提取到下面进行申明。

console.log(a) 打印的时候,因为 a 申明了然而没有填写值,所以是 undefined

而这段代码换成 let 或者 const 语句呢?就会呈现 暂时性死区

console.log(a);let a = 10;

输入:

VM196:1 Uncaught ReferenceError: Cannot access 'a' before initialization

OK,上面进行 变量晋升和暂时性死区 小总结:

  • var 定义的变量后面进行打印,因为变量晋升缘故,所以能够打印 undefined
  • letconst 定义的变量后面进行打印,因为暂时性死区问题,所以会报错。

再看看函数晋升:

var foo = 3;function getFoo() {  var foo = foo || 5;  console.log(foo); // 输入 5}getFoo();

记住一句话:

  • 函数是一等公民

所以,下面的代码,JavaScript 解析会变成:

function getFoo() {  var foo;  foo = foo || 5;  console.log(foo);}var foo;foo = 3;getFoo();

看,这就是 函数晋升

5.2 题目 1

返回目录
function getFoo() {  foo();  var foo = function() {    console.log(1);  }  foo();  function foo() {    console.log(2);  }  foo();}getFoo();

请问下面代码输入什么?


211

咱们将其换成变量和函数晋升后的成果便可得悉:

function getFoo() {  var foo;  function foo() {    console.log(2);  }  foo(); // 2  foo = function() {    console.log(1);  }  foo(); // 1  foo(); // 1}getFoo();

5.3 题目 2

返回目录
console.log(a);var a = 10;var a = 100;console.log(a);function a() {  console.log('a');}function a() {  console.log('aa');}a();

请问下面代码输入什么?


答案:

[Function: a]100100

5.4 题目 3

返回目录
var a = 1;function b() {  a = 10;  return;  function a() {}}b();console.log(a);

请问下面代码输入什么?


答案:1

5.5 题目 4

返回目录
function foo() {  function bar() {    return 3;  }  return bar();  function bar() {    return 8;  }}console.log(foo());

请问下面代码输入什么?


答案:8

5.6 题目 5

返回目录
var foo = { n: 1 };(function(foo) {  console.log(foo.n);  foo.n = 3;  foo = { n: 2 };  console.log(foo.n);})(foo)console.log(foo.n);

请问下面代码输入什么?

  • A:1 2 3
  • B:undefined 2 1
  • C:报错
  • D:1 2 1

答案:

var foo = { n: 1 };(function(foo){            // 形参 foo 同实参 foo 一样指向同一片内存空间,这个空间里的 n 的值为 1    var foo;               // 优先级低于形参,有效。    console.log(foo.n);    // 输入 1    foo.n = 3;             // 形参加实参 foo 指向的内存空间里的 n 的值被改为 3    foo = { n: 2 };        // 形参 foo 指向了新的内存空间,外面 n 的值为 2.    console.log(foo.n);    // 输入新的内存空间的 n 的值})(foo);console.log(foo.n);        // 实参 foo 的指向还是原来的内存空间,外面的 n 的值为 3.

六 判断变量

返回目录

JavaScript 判断变量的形式有:

  • typeof(variable)
  • variable instanceof Array
  • variable.constructor = Array
  • Object.prototype.toString.call(variable)

它们都有哪些区别呢?

6.1 typeof

返回目录
var num = 123;var str = 'abcdef';var bool = true;var arr = [1, 2, 3, 4];var json = { name: 'jsliang', age: 25 };var func = function () { console.log('this is function'); }var und = undefined;var nul = null;var date = new Date();var reg = /^[a-zA-Z]{5,20}$/;var error = new Error();console.log(  typeof num, // number  typeof str, // string  typeof bool, // boolean  typeof arr, // object  typeof json, // object  typeof func, // function  typeof und, // undefined  typeof nul, // object  typeof date, // object  typeof reg, // object  typeof error, // object);

typeof 能辨别的有:

  • number
  • string
  • boolean
  • undefined
  • function

检测其余类型的时候,都返回 object,不太稳固。

补充:

typeof NaN; // numbertypeof 10n; // biginttypeof Symbol(); // symbol
  • NaN 返回为 number
  • 10nBigInt 的示意办法,在数字前面追加 n 示意 BigInt,或者 BigInt(10) 也是一样的。
  • Symbol() 就是 Symbol

6.2 instanceof

返回目录

instanceof 判断原型链指向,咱们能够看一下它的实现原理:

function instanceOf(a, b) {  // 通过 typeof 判断根本类型  if (typeof a !== 'object' || b === null) {    return false;  }  // getPrototypeOf 是 Object 自带的一个办法  // 能够拿到参数的原型对象  let proto = Object.getPrototypeOf(a);  const prototype = b.prototype;  // 从以后 __proto__ 开始查找  while (proto) {        // 如果找到 null 还没有找到,返回 false    if (proto === null) {      return false;    }    // 如果 a.__proto__.xxx === b.prototype,返回 true    if (proto === prototype) {      return true;    }    // 进一步迭代    proto = proto.__proto__;  }}

instanceof 就是原型链的查找过程。

var num = 123;var str = 'abcdef';var bool = true;var arr = [1, 2, 3, 4];var json = { name: 'jsliang', age: 25 };var func = function () { console.log('this is function'); }var und = undefined;var nul = null;var date = new Date();var reg = /^[a-zA-Z]{5,20}$/;var error = new Error();console.log(  num instanceof Number, // false  str instanceof String, // false  bool instanceof Boolean, // false  und instanceof Object, // false  nul instanceof Object, // false  arr instanceof Array, // true  json instanceof Object, // true  func instanceof Function, // true  date instanceof Date, // true  reg instanceof RegExp, // true  error instanceof Error, // true)

保险起见,instanceof 能判断的有:

  • Array
  • Function
  • Date
  • RegExp
  • Error

其余的不太能确定。

6.3 constructor

返回目录
var num = 123;var str = 'abcdef';var bool = true;var arr = [1, 2, 3, 4];var json = { name: 'jsliang', age: 25 };var func = function () { console.log('this is function'); }var und = undefined;var nul = null;var date = new Date();var reg = /^[a-zA-Z]{5,20}$/;var error = new Error();function Person(){}var Tom = new Person();console.log(  Tom.constructor === Person,  num.constructor === Number,  str.constructor === String,  bool.constructor === Boolean,  arr.constructor === Array,  json.constructor === Object,  func.constructor === Function,  date.constructor === Date,  reg.constructor === RegExp,  error.constructor === Error);

失去的所有后果都是 true,除了 undefinednull,其余类型根本能够通过 constructor 判断。

不过因为 constructor 的属性是能够被批改的,可能导致检测出的后果不正确。

6.4 Array.prototype.toString.call

返回目录
var num = 123;var str = 'abcdef';var bool = true;var arr = [1, 2, 3, 4];var json = { name: 'jsliang', age: 25 };var func = function () { console.log('this is function'); }var und = undefined;var nul = null;var date = new Date();var reg = /^[a-zA-Z]{5,20}$/;var error = new Error();console.log(  Object.prototype.toString.call(num), // [object Number]  Object.prototype.toString.call(str), // [object String]  Object.prototype.toString.call(bool), // [object Boolean]  Object.prototype.toString.call(arr), // [object Array]  Object.prototype.toString.call(json), // [object Object]  Object.prototype.toString.call(func), // [object Function]  Object.prototype.toString.call(und), // [object Undefined]  Object.prototype.toString.call(nul), // [object Null]  Object.prototype.toString.call(date), // [object Date]  Object.prototype.toString.call(reg), // [object RegExp]  Object.prototype.toString.call(error), // [object Error]);

一个完满的判断办法,能够检测下面提到的所有类型,只须要将它的后果 result.slice(8, -1) 就能失去具体的类型。

七 参考文献

返回目录
  • [x] JavaScript: 变量晋升和函数晋升【浏览倡议:10min】
  • [x] 变量晋升必须留神的问题【浏览倡议:5min】
  • [x] 口试题-Js前端变量晋升的问题【浏览倡议:5min】
  • [x] 对于JS变量晋升的一些坑【浏览倡议:5min】
  • [x] JS 中对变量类型的判断【浏览倡议:10min】
  • [x] js中块级作用域以及函数作用域之间有什么区别?(代码解析)【浏览倡议:5min】

jsliang 的文档库由 梁峻荣 采纳 常识共享 署名-非商业性应用-雷同形式共享 4.0 国内 许可协定 进行许可。<br/>基于 https://github.com/LiangJunrong/document-library 上的作品创作。<br/>本许可协定受权之外的应用权限能够从 https://creativecommons.org/licenses/by-nc-sa/2.5/cn/ 处取得。