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

原型链:

 
 
  1. 实例通过__proto__ 访问对应构造函数的原型 -> B.prototype
  2. 函数原型 通过__proto__ 访问Object的原型 -> Object.prototype
  3. Object的原型的__proto__ 指向 null
  4. 构造函数 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() 产生的。


  1. Object 是所有对象的爸爸,所有对象都可以通过__proto__ 找到它
  2. Function 是所有函数的爸爸,所有函数都可以通过__proto__ 找到它
  3. Function.prototype 和 Object.prototype 是两个特殊的对象,他们由引擎来创建
  4. 除了以上两个特殊对象,其他对象都是通过构造器 new 出来的
  5. 函数的 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'

 

 

 

 

posted @ 2018-12-07 13:54  G_Owen  阅读(210)  评论(0编辑  收藏  举报