核心
- 简单类型不可变
- typeof 错误
| 作用域 | ||
|---|---|---|
| 变量名 | 哈希散列( 需要的大小/堆范围 ) | |
| aNumber | 0100 | 1234 |
| aString | 0110 | abcd |
| aObject | 0000 | d92hdh839ehfh11 |
| aArray | 0001 | d92hdh839ehfh22 |
| anotherArray | 0001 | d92hdh839ehfh22 |
| aNull | 00000000000000000000000000 |
| 内存堆 | |||
|---|---|---|---|
| aObject | aArray | anotherArray | |
| d92hdh839ehfh33 |
- 内存块满了怎么办? - 分配新的内存块,并迁移
| 对象栈 | ||
|---|---|---|
| 属性名 | 哈希散列( 需要的大小/堆范围 ) | |
| aNumber | 1 | 1234 |
| aString | 2 | abcd |
| aObject | 0000 | d92hdh839ehfh11 |
| aArray | 0001 | d92hdh839ehfh22 |
| aNull | 0000 | d92hdh839ehfh22 |
| proto | 0000 | d92hdh839ehfh55 |
| 数组 - 对象栈 | ||
|---|---|---|
| 变量名 | 哈希散列( 需要的大小/堆范围 ) | |
| 0 | 1 | 1234 |
| 1 | 2 | abcd |
| 2 | 0000 | d92hdh839ehfh11 |
| NAME | 0001 | d92hdh839ehfh22 |
| proto | 0000 | d92hdh839ehfh55 |
语法
数字
数字有 int float double,整型 浮点 双精度浮点。但 JS 不管。
NaN 是一个甚至不等于自己的数字,用于表示你写错了。
JS 内置对象 Math,用于数学运算。
字符串
js 所有字符都是 16 位的。
字符串是不可变的( number 和 boolean 也可以这样说,因为在栈内存)。
字符串拥有一些方法。
语句
每个基础类型都有被当作假的值,number 的 NaN 让数字类型拥有两个。
类型比较,是比较栈内存值。
for in
| 1 | 12345 |
|---|---|
| name | 'name' |
| father | -> |
循环一个对象的所有属性,包括原型。
创建一个迭代器,使用迭代器来遍历。
迭代器创建一个指针,指向目标的地址(也是起始地址),再每次 next 时向后移动。直到指针指向了目标之外(如何判断到了目标外)。
例如对于数组和对象,也就是复杂类型,他们是哈希型数据结构,指针指向键地址,读取值。也是因为是哈希类型,所以对于对象来说,属性的顺序是不固定的(es6 之前,因为有些引擎迭代是迭代值地址,不是key值)。
在 ECMAScript 2015 (ES6) 规范之前,JavaScript 对象的属性遍历顺序并没有被规范明确定义。不同的 JavaScript 引擎或不同的运行环境(浏览器、Node.js 等)可能会以不同的方式对对象属性进行内部实现,导致属性的遍历顺序不一致。
从 ECMAScript 2015 (ES6) 开始,规范明确了对象属性的遍历顺序应该与属性添加的顺序保持一致。也就是说,使用 for...in 循环或 Object.keys() 方法遍历对象的属性时,它们会按照属性添加的顺序进行遍历,最后遍历原型链。但需要注意的是,这仅适用于普通的数字和字符串属性,而不包括特殊类型的属性(如 Symbol 属性)或属性访问器。
数组一致是类似遍历对象从一个新的储存key值对数组中取key。
哈希的散列化,将一个任意大小的值散列化为一个固定长度的序列,输入窗口大于输出窗口,导致散列可能重复。在 JS 中使用链表法解决,当散列值作为地址,发现地址已经被使用,则在当前储存的(key,value,next),的 next ,以地址来讲,就是紧贴着当前空间进行存储。
对象
对象是可变的键值对,树状,叶子结点是简单类型。
字面量,理解为一个特定的样子,如果见到这个样子,js 会把他创建为一个对应的类型,像是一个“位置函数”。
就像[ a ] => Array(a)
原型,像是一个特殊的键,这个键的值会在使用时当作当前对象的,像是拍平了一层,而她的原型又有原型。。。知道原型的终点 null。
对key值的检索可以obj.['key'] / obj.key;
检索则是在自身和原型上找到键值,获取值,或者地址。
原型 prototype
hasOwnProperty 检查是否在本身而非原型上
函数
函数也是对象,但是多了两个属性,上下文与行为代码。
函数除了定义的参数,也隐藏式接受了 this 和 arguments 参数,也就是常说的谁调用 this 就是谁;arguments 或者说其实函数只接受了这个 arguments,而定义的参数是一个语法糖,帮你从中拿出。
arguments 是一个四不像,有 lenth ,像是数组的索引,但并没有数组的方法。它的原型是对象的原型。
函数总会返回,用 return 手动,自动 undefined,new 关键词则返回 this。
(箭头函数不管,但箭头函数原型和正常函数是一样的)
函数直接调用时,this 会取到全局,这是个bug,预期取到调用处的上下文。
箭头函数的底层原理?
块作用域与const let;
闭包
暴露上下文变量。
回收不了垃圾。
柯里化,高阶函数?
闭包的作用被块级作用域挤压的没啥用了感觉。
闭包很大一个作用是隐藏变量,但总要有一个出口,把这些变量挂在在出口属性上呗。。。
继承
一句话表达你对原型的理解:实例继承—— 不对
function myNew(Creater,...args){
//判断
if (typeof Creater !== 'function') return new Error('type err');
//生成新对象并继承
let obj = {};
let res = Creater.call(obj,...args);
obj.__proto__ = Object.create(Creater.prototype);
// Object.setPrototypeOf(obj,Creater.prototype);官方MDN表示性能差不推荐
obj.constructor = Creater;
//判断函数本身的返回是否为引用类型
let isObject = typeof res === 'object' && res !== null;
let isFunction = typeof res === 'function';
return (isFunction || isObject)? res:obj;
}
私有变量
使用闭包,函数浅拷贝一个值返回,来达到只读。
原型链继承
function A() {
this.info = {
name: "yhd",
age: 18,
};
this.getInfo = {
return this.info;
}
}
直接让子类原型指向父类实例。
let b = new B();
B.protoType = new A();
问题:引用类型共用
构造函数继承
在子类构造函数中使用父类构造函数强化。
//解决了引用共用问题
function B(){
A.call(this);
}
问题
每个子类都继承于一个独立的父类实例,大量内存浪费
所有属性方法写在构造函数中无法复用
不能跨节点继承
没有公共原型实例
组合继承
构造函数继承A实例并重写构造方法改回自己。
构造函数偷用A进行实例加强。
function B (){
A.call(this);
}
B.prototype = new A();
B.constructor = B;
问题
原型有父类实例,自己又使用父类构造函数强化,父类属性与方法覆盖重合。
但是可以跨节点继承了,可以操作原型添加方法了,可以私有引用类型了。
组合寄生式继承
使用组合继承但是原型指向操作跨过父类构造直接指向父类的原型。
function B(){
A.call(this);
}
function myExtend (son,father){
let proto = Object.create(father.prototype);
proto.constructor = son;
son.prototype = proto;
}
myExtend(B,A);
最成熟的方法。
数组
得益于对象继承,数组的每个值也是键值对,所以值可以是任何类型,或者说 js 根本没有数组,只是长得像其他语言数组的一个对象。
数组其实是数字string作为key
同理 delete 会删除这个 key
判断数组
方法
介绍了一堆 api
代码风格
写这本书的时候没有 Prettier 吗
- 缩进
- 注释
- if 的花括号等
优美特性
- 动态对象,基于原型继承
- 字面量定义
- 函数词法作用域 / 块作用域
垃圾
- 原来变量定义都写在上面是这个原因:没有块级作用域的变量提升。
- 大量保留字
- UniCode 认为一对字符是两个不同的字符 —— 没看懂
- typeof null 是 object
typeof 用于检测给定数据类型的 typeTag,枚举有以下:
- "undefined":表示未定义的值。
- "boolean":表示布尔值。
- "number":表示数字。
- "string":表示字符串。
- "symbol":表示符号。
- "function":表示函数。
- "object":表示对象(包括对象、数组、null 等)
-
- 关键词同时负责连接字符串 & 加法运算,一边是字符串则认为另一边也作为字符串。
- 0.1 + 0.2 不是 0.3
- NaN 不是常量 !?
- hasOwnProperty 不是运算符,使用 call/apply?
call / apply:判断第一个参数是否为对象,不是则使用window,使用一个 Symbol 在对象上绑定 this 函数,调用获得结果,再删掉绑定。
bind 则第一个参数不是对象时报错,返回一个等待调用的 apply,返回前手动将结果的原型设为第一个参数的原型。其中 apply 的第一个参数,考虑结果可能作为构造函数使用new调用,所以判断this 的原型是否是为结果函数本身,如果是则不绑定obj转而绑定新的对象this。
- 使用 hasownProperty 防止用字符串去值是字符串与原型方法相撞
榴莲,好吃但扎手
- 双等号,不推荐用,不会我也不学,主打一个拒绝。
- with ,没听说过呢,看起来是“优先使用”的意思
- eval 还是有立功的,各种在线测试网站
- 少一个花括号的单行语句,好不好吃不一定,肯定扎手
- 居然不让用自增自减?
- 位运算符,之前跟行见学过,忘了,js 的位运算并不快,但位运算可以补个课。//TODO
- Boolean 等,书说很奇怪的存在。但我常用于类型转换
- 没啥用的 void,是一个返回undefined的壳子,不向C表示无值,但在编译器中会按照无值类型解释。
115