JavaScript 随笔
js 内置 7种类型
基本类型: string number boolean null undefined symbol
引用类型: object
typeof 可判断除 null 外所有 基本类型
typeof null // 'object'
//JS 的最初版本中 使用的是 32 位系统,为了性能考虑使用低位存储了变量的类型信息,000 开头代表是对象,然而 null 表示为全零,所以将它错误的判断为 object
//undefined 不是保留字,能够在低版本浏览器被赋值
//判断一个值是否等于 undefined
a === void 's'
可通过Object.prototype.toString.call() 判断一个变量的正确类型
类型转化
在条件判断时,除了 undefined, null, false, NaN, '', 0, -0,其他所有值都转为 true,包括所有对象。
对象在转换基本类型时,首先会调用 valueOf 然后调用 toString。并且两个方法可以重写
let a = {
valueOf() {
return 0;
},
toString() {
return '1';
},
[Symbol.toPrimitive]() {//优先级最高
return 2;
}
}
a.valueOf() //0
a.toString() // '1'
1 + a // => 3
'1' + a // => '12'
操作符
加法运算会触发三种类型转换:将值转换为原始值,转换为数字,转换为字符串。
一方是字符串类型,会把另一个也转为字符串类型。
一方是数字, 另一方就转为数字。优先级 string>number
'1' + + '' //"10"
'1' ++ '' //Error
+ '' //0
toPrimitive(obj)等价于:先计算obj.valueOf(),如果结果为原始值,则返回此结果;否则.计算obj.toString(),如果结果是原始值,则返回此结果;否则,抛出异常
比较运算符
如果是对象,就通过 toPrimitive 转换对象 如果是字符串,就通过 unicode 字符索引来比较
"=="规则
x,y 类型相同:
x -> undefined //true
x -> null //true
x -> number
x -> NaN //false
y -> NaN //false
x == y //true
x -> +0 && y -> -0 //true
x -> -0 && y -> +0 //true
x -> string ,x和y 字符序列完全相同<长度相等,相同字符位置相同> //true
x -> Boolean x&&y = true/false //true
x和y 引用 同一对象 //true
x -> null , y -> undefiend //true
x -> undefiend , y -> null //true
x -> number ,y -> string Number(y) ==>参考上文 x -> number
x -> string , y ->number Number(x) ==> 参考上文 x -> number
x -> Boolean Number(x) ==> 参考上文 x -> number
y -> Boolean Number(y) ==> 参考上文 x -> number
x -> string || number, y -> object x ==toPrimitive(y)
x -> object && y ->string || number toPrimitive(x) ==y
[] == ![] //右: ![] 转成 true,然后取反变成 false
//左: ToPrimitive([]) == 0
// [].toString() -> ''
//结果为true
Prototype
原型链:
![](https://img2018.cnblogs.com/blog/1347172/201812/1347172-20181207112532226-144769964.png)
- 实例通过__proto__ 访问对应构造函数的原型 -> B.prototype
- 函数原型 通过__proto__ 访问Object的原型 -> Object.prototype
- Object的原型的__proto__ 指向 null
- 构造函数 Object.prototype 通过constructor访问构造函数本身 每个构造函数通过 prototype 访问原型
基本上所有函数都有这个属性 (只有函数才拥有prototype属性) ,有一个例外
let fun = Function.prototype.bind()
当声明一个函数时自动创建 prototype 属性, 这个属性的值是一个对象(也就是原型),只有一个属性 constructor;
constructor 是一个公有且不可枚举的属性。一旦我们改变了函数的 prototype ,那么新对象就没有这个属性了(当然可以通过原型链取到 constructor)。
function A(){}
console.log(A.prototype) // {constructor: ƒ}constructor: ƒ A()__proto__: Object
A.prototype="a"
console.log(A.prototype) //"a"
constructor作用:
- 让实例对象知道是什么函数构造了它
- 如果想给某些类库中的构造函数增加一些自定义的方法,就可以通过 xx.constructor.method 来扩展
__proto__
每个对象都有的隐式原型属性,指向创建该对象的构造函数的原型,实际指向[[prototype]], 内部属性,我们并不能访问到所以使用 proto 来访问。
console.log({})
//__proto__: Objectconstructor: ƒ Object()hasOwnProperty: ....
当我们使用 new 操作符时,生成的实例对象拥有了 __proto__属性。
function Foo() {}
// 这个函数是 Function 的实例对象
// function 就是一个语法糖
// 内部调用了 new Function(...)
所有对象都可以通过原型链最终找到 Object.prototype ,虽然 Object.prototype 也是一个对象,但是这个对象却不是 Object 创造的,而是引擎自己创建了 Object.prototype 。
所以可以这样说,所有实例都是对象,但是对象不一定都是实例。
首先引擎创建了 Object.prototype ,然后创建了 Function.prototype ,并且通过__proto__ 将两者联系了起来。
Function.prototype 以后才有了 function Function() ,然后其他的构造函数都是 function Function() 生成的。
函数通过 new Function() 生成, 不是所有函数都是 new Function() 产生的。
- Object 是所有对象的爸爸,所有对象都可以通过__proto__ 找到它
- Function 是所有函数的爸爸,所有函数都可以通过__proto__ 找到它
- Function.prototype 和 Object.prototype 是两个特殊的对象,他们由引擎来创建
- 除了以上两个特殊对象,其他对象都是通过构造器 new 出来的
- 函数的 prototype 是一个对象,也就是原型 对象的__proto__ 指向原型,__proto__将对象和原型连接起来组成了原型链
new 的过程
新生成了一个对象 链接到原型 绑定 this 返回新对象
function create() {
// 创建一个空的对象
let obj = new Object()
// 获得构造函数
let B = [].shift.call(arguments)
// 链接到原型
obj.__proto__ = B.prototype
// 绑定 this,执行构造函数
let result = B.apply(obj, arguments)
// 确保 new 出来的是个对象
return typeof result === 'object' ? result : obj
}
实例对象,都是通过 new 产生的,无论是B() 还是obj 。
所有运算圆括号优先级最高 成员 - 计算 从左至右访问
new F1.getName(); //-->new (F1.name());
new F1().getName(); // -->(new F1()).name();
instanceof
可以正确的判断对象的类型,因为内部机制是通过判断对象的原型链中是不是能找到类型的 prototype
。
function objType(left, right) {
// 获得类型的原型
let prototype = right.prototype
// 获得对象的原型
left = left.__proto__
// 判断对象的类型是否等于类型的原型
if (left === null)
return false
if (prototype === left)
return true
left = left.__proto__
}
执行上下文
当执行 JS 代码时,会产生三种执行上下文
- 全局执行上下文
- 函数执行上下文
- eval 执行上下文
arguments 是函数独有的对象(箭头函数没有)
作用域链,可以把它理解成包含自身变量对象和上级变量对象的列表,通过 [[Scope]]
属性查找上级变量
在生成执行上下文时,会有两个阶段。
函数和变量提升
第一个阶段是创建的阶段,JS 解释器会找出需要提升的变量和函数,并且给他们提前在内存中开辟好空间,函数的话会将整个函数存入内存中,变量只声明并且赋值为 undefined,
第二个阶段,也就是代码执行阶段,我们可以直接提前使用。
在提升的过程中,相同的函数会覆盖上一个函数,并且函数优先于变量提升
b() // 1 function b() { console.log('2') } function b() { console.log('1') } var b = 'Hello world'