Linjiayu6 / FE-Notes

[2020] Front-End Notebook
13 stars 2 forks source link

10 - JS 2.0 #10

Open Linjiayu6 opened 4 years ago

Linjiayu6 commented 4 years ago

1. ['1', '2', '3'].map(parseInt) what & why ?

// 展开后
['1', '2', '3'].map((data, index) => parseInt(data, index))
所以 编程了这样
- parseInt('1',  0)  
- parseInt('2',  1)
- parseInt('3',  2)
['10','10','10','10','10'].map(parseInt)
// 结果: [10, NaN, 2, 3, 4]
['1', '2', '3'].map((value) => parseInt(value)) 这样写就没问题了
Linjiayu6 commented 4 years ago

2. es5 / es6 继承除了写法上 有什么其他区别吗?

1. 🔥 继承差异

es5 继承利用 Child.proto === Function.prototype

function Parent () {}
function Child () {}

Child.prototype = Object.create(Parent.prototype)
Child.prototype.constructor = Child
const c = new Child()
/**
 * 实例对象 
 * __proto__: 普通对象 / 函数对象
 * prototype: 只有函数对象里才有
 * 实例对象__proto__ 指向 prototype
 */
c.__proto__ === Child.prototype // true
Child.__proto__ === Function.prototype // true
Child.__proto__ === Parent.__proto__ // true

es6 Child.proto === Parent

class Parent {}
class Child extends Parent{ constructor (props) { super(props) } }
const c = new Child()
// 直接寻址方式
Child.__proto__ === Parent

2. 🔥 this 时机不同

Linjiayu6 commented 4 years ago

3. 三个判断数组的方法 优劣势?

// 1. [所有数据类型都能判断]
Object.prototype.toString.call([1, 2]) // ['object Array']

// 2. 实例对象 是否是 该类 __proto_ 指向 Class.prototype 
// 在原型链上是否能找到该原型, [只能判断对象类型]
[1, 2, 3] instanceof Array // true
[1, 2, 3] instanceof Object // true

// 3. 新增方法, 当浏览器不支持则用Object.prototype.toString.call判断
Array.isArray([1, 2, 3]) //  true
Linjiayu6 commented 4 years ago

4. 模块化 Common.js / es6

模块本质: 管理文件

es6 module vs common.js

输出不同

说明: export default / module.exports
common.js: (Node.js Webpack)
- [值的拷贝], 第一次加载运行一次, 并[缓存]下来了, 再次加载直接读缓存内容。
- 一旦输出, 模块 [内部变化] 不会影响之前输出

es6 module: 
- [值的只读引用], 不会有缓存值情况。
- 模块 [内部变化], 加载其模块也会变。

加载方式不同

说明: import / require
common.js: [运行时加载] 
- 加载 整个对象(module.exports = 对象), 该对象只有在运行完才生成。

es6 module: [编译时加载] 
- 仅输出暴露的代码块, 不用加载整个模块
- 代码静态解析依赖就引入了代码, 不是在运行时候。
Linjiayu6 commented 4 years ago

5. let / const 不挂载到window下, 那是去哪儿了?

let a = 1
const b = 2
var c = 3 // 其实是挂载到了window window.c = 3

// 类似函数块级作用域, 外层window无法访问到
(function () {
    var a = 1
    var b = 2
})()
Linjiayu6 commented 4 years ago

6. 值输出?

🔥非匿名函数自执行, 函数名只读

// 第一道题
var b = 10; // 在window下
// 🔥非匿名函数自执行, 函数名只读
(function b() {
  b = 20; 
  // 没有var说明 在作用域链上找b, 找到function b(){} 
  // b函数相当于const, 内部是无法赋值的, 所以无效赋值
  console.log(b) // 在作用域链上 最近找到的是 🔥输出function b
  console.log(window.b) // 🔥输出10
})()

// 第二道题
var b = 10;
(function b() {
  'use strict'
  b = 20 // 报错 b找到了function b, 相当于const类型, 严格模式下直接报错
  console.log(b)
})()

// 第三道题
var b = 10; // 在window下
(function a() {
  b = 20; // 没有var说明, 都在window下 window.b = 20
  console.log(b) // 所以输出的是 20
})()

// 第四道题
var b = 10;
(function b() {
    window.b = 20; 
    console.log(b); // [Function b]
    console.log(window.b); // 20
})();

// 第五道题
var b = 10;
(function b() {
    var b = 20; // 内部变量
    console.log(b); // 20
    console.log(window.b); // 10 
})();
Linjiayu6 commented 4 years ago

7. 值输出?

var a = 10;
(function () {
    console.log(a)
    a = 5 
    console.log(a)
    console.log(window.a)
    var a = 20;
    console.log(a)
})()

/**
 * 输出: undefined -> 5 -> 10 -> 20
 * 函数体内, var a = 20 声明提升
 *  var a = undefined;
 *  console.log(a) // undefined
    a = 5 // 赋值 没有定义? 会在作用域链上找, 是否声明, 没有再往上找
    console.log(a) // 5
    console.log(window.a) // 10
    a = 20; // 赋值
    console.log(a) // 20
 */
var a = 123;
(function(){
  console.log(a)
  a = 456
}());
console.log(a)
// 输出 123, 456

// 局部值, 全局无法访问
(function(){
  var a = 456
}());
console.log(a) // Error: a is not defined

// 当前作用域无该值, 则挂载到window下
(function(){
   a = 456
}());
console.log(a) // 456
Linjiayu6 commented 4 years ago

8 - 改造 让其打印出10 或 20

var b = 10;
(function b(){
    b = 20;
    console.log(b); 
})();
var b = 10;
(function b(){
    b = 20; // 无效赋值, 在作用域上找到了function b, 因为是自己运行, 只读
    console.log(this.b) // this指向window
    console.log(window.b);  // 10
})();

var b = 10;
(function b(){
    var b = 20;
    console.log(b); // 20
})();

var b = 10;
(function b(){
    b = 20;
    console.log(b); // 20
    var b = 0
})();
})();
Linjiayu6 commented 4 years ago

9 - [3, 15, 8, 29, 102, 22].sort();?

[3, 15, 8, 29, 102, 22].sort()
// 默认是按照字母数组顺序排序的 [102, 15, 22, 29, 3, 8]

[3, 15, 8, 29, 102, 22].sort((a, b) => a - b) // a > b 位置交换 其他位置不变
// [3, 8, 15, 22, 29, 102]
[3, 15, 8, 29, 102, 22].sort((a, b) => b - a)
// [102, 29, 22, 15, 8, 3]

10 - call 和 apply 的区别是什么,哪个性能更好一些?

call 会更优秀一些, 区别在于传参不同, apply 传入数组, 还需要多一层对参数解构

11 - 值输出?

var obj = {
  '2': 3,
  '3': 4,
  'length': 2,
  'splice': Array.prototype.splice,
  'push': Array.prototype.push
}
obj.push(1)
obj.push(2)
console.log(obj) // [, , 1, 2]

/**
 * push做了两件事情
 * 1. push数据到数组末尾
 * 2. 返回数组新长度
 * 
 * 1. push(1): obj[2] = 1, obj.length += 1
 * 2. push(2): obj[3] = 2, obj.length += 1
 * 因为数组没有设置 0, 1 下标值, 所以是empty打印
 * 类数组返回: [, , 1, 2]
 * Object[
 *   2: 1,
 *   3: 2,
 *   'length': 2,
     'splice': Array.prototype.splice,
     'push': Array.prototype.push
 * ]
 */
Linjiayu6 commented 4 years ago

12 - 值输出?

var a = { n: 1 };
var b = a;
a.x = a = { n: 2 };

console.log(a.x)    
console.log(b.x)

/**
 * a.x = a = {n : 2}
 * 赋值是从右到左, 但是 .的优先级比 = 高, 所以会先执行 a.x
 * a = { n: 1, x: undefined } x是新增的属性
 * 接下来是值的赋值, 从右到左
 * a 新的指向 => {n : 2}
 * a.x => {n : 2}
 * 
 * 输出: undefined, {n : 2}
 */

image

Linjiayu6 commented 4 years ago

13 - a.b.c.d 和 a['b']['c']['d'],哪个性能更高?

从AST来看 a['b']['c']['d'] 比 a.b.c.d 会解析 [ ]。解析的话 前面比后面更好些。所以性能更高。

14 - 箭头函数 ?

所以在箭头函数体内是

15 - 为什么普通 for 循环的性能远远高于 forEach 的性能,请解释其中的原因?

对for来说 最原始的循环方式
forEach((value, index) => {}) 需要额外的函数调用 和 内存空间
Linjiayu6 commented 4 years ago

16 - 值?

// 例子1 ---------------------
var a={}, b='123', c=123;  
a[b]='b';
a[c]='c';  
console.log(a[b]);
/**
 * a[b]='b'; a: { '123': 'b' }
 * a[c]='c'; c=123 对于Object来说, 被转换成string类型
 * a: { '123': 'c' }
 * 输出: 'c'
 */

// 例子2 ---------------------
var a={}, b=Symbol('123'), c=Symbol('123');  
a[b]='b';
a[c]='c';  
console.log(a[b]); // b
console.log(a[c]); // c
/**
 * Symbol 是唯一, 任何类型值都不相等, 前一个不会被覆盖掉
 * a = { Symbol(123): 'b' }
 * a = { Symbol(123): 'b', Symbol(123): 'c' }
 * 输出: 'b', 'c'
 */

// 例子3  容易错 !!! 多看 ---------------------
var a={}, b={key:'123'}, c={key:'456'};  
a[b]='b';
a[c]='c';  
console.log(a[b]);
/**
 * 这种思路是错的 ❌
 * a: { '{key:'123'}': 'b' }
 * a: { '{key:'123'}': 'b', '{key:'456'}': 'c' }
 * 
 * 除了string 和 Symbol外, 会调用toString()
 * {key:'123'}.toString() 会变成 '[object Object]'
 * {key:'456'}.toString() 也会变成 '[object Object]'
 * [1, 2, 3].toString() 变成 "1,2,3"
 * a: { [object Object]: 'c' }
 * 输出 c
 */
Linjiayu6 commented 4 years ago

17 - var、let 和 const 区别的实现原理是什么?

1. 声明 / 初始化 / 赋值

- var 是 声明 + 初始化, 执行是赋值
- let / const / class 是 声明, 执行 初始化 + 赋值

var 有变量提升和声明, 会在初始化的时候,在EC创建该变量, 并赋值undefined. 等到执行才填如值
但像let/const/class 都是进入一个块级作用域,只会声明,但是不会赋值undefined,  
此时如果在此作用域提前访问,则报错xx is not defined。等到执行到这一步的时候,才会初始化和赋值。

2. 内存分配

- var 会现在栈内存中 预先分配 内存空间
- let / const 不会 预先 在占内存分配空间
Linjiayu6 commented 4 years ago

18 - 值结果?

function changeObjProperty(o) {
  // o是个引用, 相当于 传了 webSite
  // webSite.siteUrl = 'baidu'
  o.siteUrl = "http://www.baidu.com"

  o = new Object() // o = {} 新的对象, 不同于传参的o
  o.siteUrl = "http://www.google.com"
  return o
} 

let webSite = new Object();
let twowebSite = changeObjProperty(webSite);
console.log(webSite.siteUrl); // baidu
console.log(twowebSite.siteUrl); // google
let a = 1
// 基本类型是copy, 里面影响不了外面
function changeObjProperty(a) {
  console.log(a) // 1
  a = "http://www.baidu.com"
  console.log(a) // http://www.baidu.com
}

changeObjProperty(a)
console.log(a) // 1
Linjiayu6 commented 4 years ago

19 - 值结果?

function Class () {}
Class.a = xxx // 静态方法
Class.prototype.b = xxx // 原型方法

class A {
  static a () {} // 静态方法 是类可以直接访问到
  b () {}
}
function Foo () {
  // 构造函数 
  Foo.a = function() { // 静态方法 a
    console.log(1)
  }
  this.a = function() {
    console.log(2)
  }
}
console.log(Foo.a) // undefined, Foo.a(1) 是在构造函数里的创建

// 挂在原型方法
Foo.prototype.a = function() {
  console.log(3)
}
console.log(Foo.prototype.a) // Foo.prototype.a(3)
console.log(Foo.a) // undefined

// 直接挂在方法
Foo.a = function() {
  console.log(4)
}
console.log(Foo.a) // Foo.a(4)

Foo.a(); // 4
let obj = new Foo();
obj.a(); // 2 在构造函数方法里定义 this.a
// 实例对象有私有属性a 和 原型链属性a, 私有优先级更高
// 私有a 会把 共有给覆盖掉

Foo.a(); // 1 构造函数执行 (new Foo()) 已经替换了 4的那个输出
Linjiayu6 commented 4 years ago

20 - 值结果?

// String('11') 返回的是 '11'
'11' === String('11') // true

// == 做了隐式转换, 调用toString 
// 类似: String('11') == new String('11').toString()
// new String('11') 返回的是个对象 { 0: 1, 1: 1 }
String('11') == new String('11') // true 值相同

String('11') === new String('11') // false

21 - 值结果?

var name = 'Tom';
(function() {
    // var name = 'Jack'; 会提升到这里
    console.info('name', name); // undefined
    console.info('typeof name', typeof name); // undefined
    if (typeof name == 'undefined') {
        var name = 'Jack';
        console.log('Goodbye ' + name); // Goodbye Jack
    } else {
        console.log('Hello ' + name);
    }
})();
var name = 'Tom';
(function() {
  if (typeof name == 'undefined') {
          let name = 'Jack';
          console.log('Goodbye ' + name);
      } else {
          console.log('Hello ' + name);
      }
  })();

// Hello Tom
var name = 'Tom';
(function() {
if (typeof name == 'undefined') {
  name = 'Jack'; // 未声明 会被提升到全局作用域中, 没有变量提升效果 window.name 会访问到
  console.log('Goodbye ' + name);
} else {
  console.log('Hello ' + name);
}
})();

// Hello Tom
Linjiayu6 commented 4 years ago

22 - 值结果?

1 + "1" 

2 * "2"

[1, 2] + [2, 1]

"a" + + "b"

/**
 * 加法: 有一个是string, 都转为string 
 *      "1" + 1 = '11' "1" + "1" = '11'
 * 乘法: 都转为number
 *      '2' * '3' = 6 '2' * 3 = 6
 * 对象: 先转换为 toString 
 * [1, 2].toString() = '1, 2'
 * [2, 1].toString() = '2, 1'
 * 
 * + '1' string 转为 number: 1
 * + 'a' string 转为 number: NaN
 * 
 * 结果:
 * '11'
 * 4
 * '1, 22,1'
 * 'aNaN'
 */
Linjiayu6 commented 4 years ago

23 - 值结果?

async function main() { console.time(); const x = wait(); // 已经执行promise const y = wait(); // 已经执行promise const z = wait(); // 已经执行promise await x; await y; await z; console.timeEnd(); } main();


-  3 * 10 * 1000   大于30多s。
```javascript
function wait() {
  return new Promise(resolve =>
    setTimeout(resolve, 10 * 1000)
  )
}

async function main() {
  console.time();
  await wait();
  await wait();
  await wait();
  console.timeEnd();
}
main();