一些相似属性或函数的区别

220 阅读4分钟

Promise与Async_await的区别

Async_await是generator和Promise的语法题。

Async、await优化JS的堆栈处理

与直接使用Promise相比,使用Async/await不仅提高了代码的可读性,同时也优化了JS引擎的执行方式

最大的区别在于: await fn() 会暂停其所在的async函数的执行,而Promise.then(b)会将b加入到回调链中,继续执行当前函数。

Promise.then()

const a = () => {
    b().then(() => c())
}

调用a()函数时,这些事情同步发生,b()函数返回一个promise对象,调用then方法,Promise会在将来的某个时刻resolve,也就是把then里的回调函数添加到回调链。

这样,a()函数就执行完了,在这个过程中,a()函数并不会暂停,因此在异步函数resolve的时候,a()的作用域已经不存在了

为了解决这个问题,JavaScripts引擎要做一些额外的工作;它会及时记录并保存堆栈信息。对于V8引擎来说,这些堆栈信息随着Promise在Promise链中传递,这样c()函数在需要的时候也能获取堆栈信息。但是这无疑造成了额外的开销,会降低性能;保存堆栈信息会占用额外的内存。

Await

const a = async () => {
    await b()
    c()
}

当b()函数执行的时候,a()函数被暂停了,因此a()函数的作用域还在内存可以访问

如果b()抛出一个错误,堆栈通过指针迅速生成。如果c()函数抛出一个错误,堆栈信息也可以像同步函数一样生成,因为c()是在a()中执行的。不论是b()还是c(),我们都不需要去存储堆栈信息,因为堆栈信息可以在需要的时候立即生成。而存储指针,显然比存储堆栈更加节省内存。

为了让JavaScript引擎处理堆栈的方式性能更高,请尽量使用Async/await,而不是直接使用Promise

var、let和const的区别

let 和 const 是 ECMAScript6 新推出的特性,其中 let 是能够替代 var 的“标准”,所以我们探讨 var、let 和 const 的区别

var

变量提升

var是JavaScript用来定义变量的一个关键字,这是一个简单的变量定义方式;即使在定义之前调用,也不会报错。

console.log(a); // undefined
var a = 0;
console.log(a); // 0

输出undefined的时候并不是我们想要的值;上面的代码其实是按下面这样执行的,这样就叫做变量提升

var a;
console.log(a); // undefined
a = 0;
console.log(a); // 0

这就是为什么第二行 a 会打印出 undefined 的原因了。

var的作用域

for(var i = 0; i < 5; i++){
    var a = 0;
}
console.log(i); // 5
console.log(a); // 0

定义在for的循环语句中的 i 和 a变量,能够在语句之外能够获取到,显然很不规范。

经典题目

for (var i = 0; i < 5; i++) {
    setTimeout(function () {
        console.log(i); // 5 5 5 5 5 
    }, 300);
}

i在全局范围内都有效,在setTimeout函数中没有找到i的定义,所以在全局里才能找到变量i,在for循环执行完之后,事件循环中的setTimeout中的匿名函数开始执行,这个时候的全局i其实已经是5了,所以全部打印出了5。

每个循环共用一个 i 变量,所以最后一次更改就是 i 的值。

for (var i = 0; i < 5; i++) {
    (function (i) {
        setTimeout(function () {
            console.log(i) // 0 1 2 3 4
        }, 300)
    })(i)
}

使用闭包,每次调用都让其留在每次循环的执行函数中,这样能让每次循环都是在自己作用域的 i 变量。

var能重复定义

var是允许多次定义的,并且不会报错

var a = 0;
console.log(a); // 0
var a = 1;
console.log(a); // 1

let

暂存死区

不允许在let定义变量前使用这个变量;let的声明不会提升到执行上下文的顶部,从所在的块级作用域开始,到初始化位置,称为“暂存死区”,在这个区域调用let声明的变量会报错。

console.log(a); // ReferenceError: a is not defined
let a = 0;
console.log(a); // 0

块级作用域

let声明的变量拥有块作用域(注意:大括号就是块级作用域),在此之外是访问不到变量的

for (let j = 0; j < 5; j++) {
    setTimeout(function () {
        console.log(j); // 0 1 2 3 4
    }, 300);
}

循环体中的 j 并不共用,都在自己所在的块级作用域。

禁止重复定义

let a = 0;
console.log(a); // 0
let a = 1;
console.log(a); // SyntaxError: Identifier 'a' has already been declared

window对象

在 HTML 中, 全局作用域是针对 window 对象,var关键字定义的全局作用域变量属于 window 对象,而let定义的不属于。注意这是在浏览器环境下,如果是Node则无window对象。

var a = 0;
console.log(window.a) // 0
let b = 1;
console.log(window.b) // undefined

const

其实const和let非常非常类似,let该有的特性,const都有

不同

  • const不允许“修改”变量
  • const必须初始化,而let不需要
const a = 0;
a = 1; // TypeError: Assignment to constant variable
​
const b; // SyntaxError: Missing initializer in const declaration

const 不允许修改只是不能修改变量所在的地址,当定义的对象是数组或者是对象是,能够修改其中的属性。

const初始化定义后,不可使用 = 不能重新赋值

const a = {}
a.b = 1;
console.log(a) // { b: 1 }
a = 1 // TypeError: Assignment to constant variable

typeof 和 instanceof的区别

typeof是一个操作符而不是函数,用来检测给定变量的数据类型。

instanceof 用来比较一个对象是否为某一个构造函数的实例。注意,instanceof运算符只能用于对象,不适用原始类型的值。

typeof null // 'object'
typeof undefined; // "undefined"
typeof false; // "boolean"
typeof 1; // "number"
typeof '1'; // "string"
typeof {}; // "object" 
typeof []; // "object" 
typeof new Date(); // "object"typeof Symbol(); // "Symbol"
typeof 123n // 'bigint'function foo() {};
typeof foo; // 'function'
function Car(make, model, year) {
  this.make = make;
  this.model = model;
  this.year = year;
}
const auto = new Car('Honda', 'Accord', 1998);
​
console.log(auto instanceof Car);
// expected output: trueconsole.log(auto instanceof Object);
// expected output: true
// Object 是所有对象的父对象

原理

typeof

typeof原理: 不同的对象在底层都表示为二进制,在Javascript中二进制前(低)三位存储其类型信息

  • 000: 对象
  • 010: 浮点数
  • 100:字符串
  • 110: 布尔
  • 1: 整数
typeof []  // "object"
typeof null // "object"

typeof null 为"object", 原因是因为 不同的对象在底层都表示为二进制,在Javascript中二进制前(低)三位都为0的话会被判断为Object类型,null的二进制表示全为0,自然前三位也是0,所以执行typeof时会返回"object"。

instanceof

在对象的原型链上寻找与监测对象匹配的对象

instanceof的语法:

object instanceof constructor
// 等同于
constructor.prototype.isPrototypeOf(object)
  • object: 要检测的对象
  • constructor:某个构造函数

instanceof的代码实现。

function instanceof(L, R) { //L是表达式左边,R是表达式右边
    const O = R.prototype;
    L = L.__proto__;
    while(true) {
        if (L === null)
            return false;
        if (L === O) // 这里重点:当 L 严格等于 0 时,返回 true 
            return true;
        L = L.__proto__;
    }
}

instanceof原理: 检测 constructor.prototype是否存在于参数 object的 原型链上。instanceof 查找的过程中会遍历object的原型链,直到找到 constructorprototype ,如果查找失败,则会返回false,告诉我们,object 并非是 constructor 的实例。

src 、href区别

URL的概念: 统一资源定位符(或称统一资源定位器/定位地址、URL地址等,英语:Uniform Resource Locator,常缩写为URL),有时也被俗称为网页地址(网址)。 如同在网络上的门牌,是因特网上标准的资源的地址(Address)。

URL就是资源的地址

绝对URL: 显示文件的完整路径,这意味着绝对URL本身所在的位置与被引用的实际文件的位置无关

相对URL: 以包含URL本身的文件夹的位置为参考点,描述目标文件夹的位置。

以下为建立路径所使用的几个特殊符号,及其所代表的意义。

(1) .:代表目前所在的目录,相对路径。 如: <a>文本 </a><img src="./abc" />

(2) ..:代表上一层目录,相对路径。 如: <a>文本 </a><img src="../abc" />

(3) ../../:代表的是上一层目录的上一层目录,相对路径。 如: <img src="../../abc" />

(4) /:代表根目录,绝对路径。 如:[文本] (/abc)<img src="/abc" />

href

概念: 指定网络资源的位置,从而在当前元素或者当前文档和由当前属性定义的需要的锚点或资源之间定义一个链接或者关系。

白话: 与目的资源地址建立连接

src

概念: 指向外部资源的位置,指向的内容将会应用到文档中当前标签所在位置。

白话: 将资源拿过来用

src和href的区别

请求资源

  • href 指向网络资源所在位置,建立和当前元素(锚点)或当前文档(链接)之间的联系。
  • 请求 src 资源时会将其指向的资源下载并应用到文档中,比如 JavaScript 脚本,img 图片

作用结果

  • href 用于在当前文档和引用资源之间确立联系;
  • src 用于替换当前内容

解析方式

  • 当浏览器解析到 src,会暂停其他资源的下载和处理, 直到将该资源加载、编译、执行完毕,图片和框架等也如此,类似于将所指向资源应用到当前内容。这也是为什么建议把 js 脚本放在底部而不是头部的原因。
  • href,用css的link链接做例子,浏览器会识别该文档为 CSS 文件,就会并行下载资源并且不会停止对当前文档的处理。

link 和 @import的区别

两者都是外部引用 CSS 的方式,但是存在一定的区别:

(1)link是XHTML标签,除了能够加载CSS,还可以定义RSS等其他事务;而@import属于CSS范畴,只可以加载CSS。

(2)link引用CSS时,在页面载入时同时加载;@import需要页面完全载入以后再加载。(link是href属性,@import是src引入)

(3)link是XHTML标签,无兼容问题;@import则是在CSS2.1提出的,低版本的浏览器不支持。

(4)link支持使用Javascript控制DOM改变样式;而@import不支持。