Open Linjiayu6 opened 4 years ago
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
class Parent {}
class Child extends Parent{ constructor (props) { super(props) } }
const c = new Child()
// 直接寻址方式
Child.__proto__ === Parent
// 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
模块本质: 管理文件
输出不同
说明: export default / module.exports
common.js: (Node.js Webpack)
- [值的拷贝], 第一次加载运行一次, 并[缓存]下来了, 再次加载直接读缓存内容。
- 一旦输出, 模块 [内部变化] 不会影响之前输出
es6 module:
- [值的只读引用], 不会有缓存值情况。
- 模块 [内部变化], 加载其模块也会变。
加载方式不同
说明: import / require
common.js: [运行时加载]
- 加载 整个对象(module.exports = 对象), 该对象只有在运行完才生成。
es6 module: [编译时加载]
- 仅输出暴露的代码块, 不用加载整个模块
- 代码静态解析依赖就引入了代码, 不是在运行时候。
let a = 1
const b = 2
var c = 3 // 其实是挂载到了window window.c = 3
// 类似函数块级作用域, 外层window无法访问到
(function () {
var a = 1
var b = 2
})()
🔥非匿名函数自执行, 函数名只读
// 第一道题
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
})();
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
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
})();
})();
[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]
call 会更优秀一些, 区别在于传参不同, apply 传入数组, 还需要多一层对参数解构
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
* ]
*/
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}
*/
从AST来看 a['b']['c']['d'] 比 a.b.c.d 会解析 [ ]。解析的话 前面比后面更好些。所以性能更高。
一般函数是 运行时所在对象里
箭头函数是 定义时所在的对象里 用的是 它的上一层的this
所以在箭头函数体内是
对for来说 最原始的循环方式
forEach((value, index) => {}) 需要额外的函数调用 和 内存空间
// 例子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
*/
1. 声明 / 初始化 / 赋值
- var 是 声明 + 初始化, 执行是赋值
- let / const / class 是 声明, 执行 初始化 + 赋值
var 有变量提升和声明, 会在初始化的时候,在EC创建该变量, 并赋值undefined. 等到执行才填如值
但像let/const/class 都是进入一个块级作用域,只会声明,但是不会赋值undefined,
此时如果在此作用域提前访问,则报错xx is not defined。等到执行到这一步的时候,才会初始化和赋值。
2. 内存分配
- var 会现在栈内存中 预先分配 内存空间
- let / const 不会 预先 在占内存分配空间
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
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的那个输出
// 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
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
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'
*/
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。
```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();
1. ['1', '2', '3'].map(parseInt) what & why ?