Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

10 - JS 2.0 #10

Open
Linjiayu6 opened this issue Jul 9, 2020 · 17 comments
Open

10 - JS 2.0 #10

Linjiayu6 opened this issue Jul 9, 2020 · 17 comments

Comments

@Linjiayu6
Copy link
Owner

Linjiayu6 commented Jul 9, 2020

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

  • 结果: [1, NaN, NaN] ??? 满脸问号???
  • parseInt(value, 进制) 默认是10进制
// 展开后
['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
Copy link
Owner Author

Linjiayu6 commented Jul 9, 2020

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 时机不同

  • es5 先生成子类实例对象, 再调用父类构造函数 修饰 子类实例
  • es6 先生成父类实例对象, 再子类构造函数中 执行super() 来 修饰 父类实例

@Linjiayu6
Copy link
Owner Author

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

  • Object.prototype.toString.call()
  • instanceof
  • Array.isArray()
// 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
Copy link
Owner Author

Linjiayu6 commented Jul 9, 2020

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
Copy link
Owner Author

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
Copy link
Owner Author

Linjiayu6 commented Jul 9, 2020

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
Copy link
Owner Author

Linjiayu6 commented Jul 9, 2020

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
Copy link
Owner Author

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
Copy link
Owner Author

Linjiayu6 commented Jul 10, 2020

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
Copy link
Owner Author

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
Copy link
Owner Author

Linjiayu6 commented Jul 10, 2020

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

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

14 - 箭头函数 ?

  • 箭头函数与普通函数(function)的区别是什么?
  • 构造函数(function)可以使用 new 生成实例,那么箭头函数可以吗?为什么?
一般函数是 运行时所在对象里
箭头函数是 定义时所在的对象里 用的是 它的上一层的this

所以在箭头函数体内是
- 没有自己的this
- 所以不能new, 
- 不能bind/apply等操作
- 没有arguments类数组

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

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

@Linjiayu6
Copy link
Owner Author

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
Copy link
Owner Author

Linjiayu6 commented Jul 10, 2020

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
Copy link
Owner Author

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
Copy link
Owner Author

Linjiayu6 commented Jul 10, 2020

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
Copy link
Owner Author

Linjiayu6 commented Jul 10, 2020

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
Copy link
Owner Author

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
Copy link
Owner Author

Linjiayu6 commented Jul 10, 2020

23 - 值结果?

  • 大于10多s。时间是稍微比10 * 1000 大点, 但绝对不是 3101000
function wait() {
  return new Promise(resolve => setTimeout(resolve, 10 * 1000))
}

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。
function wait() {
  return new Promise(resolve =>
    setTimeout(resolve, 10 * 1000)
  )
}

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

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant