mileOfSunshine / blog

2 stars 0 forks source link

Javascript #10

Open mileOfSunshine opened 5 years ago

mileOfSunshine commented 5 years ago

? 数据类型

? if条件不成立的几个值 false, null, undefined, "", 0 NaN

? 为什么给对象添加的方法能用在基本类型上? . 运算符提供了装箱操作,它会根据基础类型构造一个临时对象,使得我们能在基础类型上调用对应对象的方法。

? 装箱 & 拆箱

mileOfSunshine commented 5 years ago

+操作符隐式类型转换

加号操作符的两个操作数如果都是值类型

// Example1:
console.log(1 + 2); // 3

// Example2:
console.log( 1 + "3"); // "13"
console.log("3" + 1); // "31"
console.log(1 + 2 +"3");  // "33"
console.log("3" + "4");  // "34"

// Example3:
console.log(1 + null);  // 1
console.log(1 + undefined ); // NaN
console.log(1 + NaN); // NaN

// Example4:
console.log("3" + null); // "3null"
console.log("3" + undefined ); // "3undefined"
console.log("3" + NaN); // "3NaN"
// Example5:
console.log(1 + {}); // "1[object Object]"
console.log(1 + []); // "1"
mileOfSunshine commented 5 years ago

This的指向

this 与函数本身以及上下文都没有关系,只取决于函数是如何被调用的

补充: 6e91eff3-06bd-473b-a7cc-051c4a02cc83

mileOfSunshine commented 5 years ago

new Date("2019-01-01") 不兼容safari, IE

建议使用

new Date('2019/01/01')
// 或
new Date(2019, 0, 1)
mileOfSunshine commented 5 years ago

apply & call & bind

fun.apply(thisArg, [argsArray])

fun.call(thisArg, arg1, arg2, ...)

apply 和 call 的功能是一样的,只是传入的参数列表形式不同. apply:最多只能有两个参数——新this对象和一个数组 argArray。 call:它可以接受多个参数,第一个参数与 apply 一样,后面则是一串参数列表。

thisArg 在 fun 函数运行时指定的 this 值

function fun() {
  console.log(this)
}

fun.apply(this) // => Window

fun.apply(fun) // => fun () { console.log(this) }

fun.apply({}) // => {}

fun.apply() // => Window

手写:

Function.prototype.myCall = function (context) {
  if (typeof this !== 'function') {
    throw new Error('Type Error')
  }

  context = context || window
  const fnSymbol = Symbol()
  context[fnSymbol] = this
  const args = [...arguments].slice(1)
  const result = context[fnSymbol](...args)
  delete context[fnSymbol]
  return result
}
Function.prototype.myApply = function (context) {
  if (typeof this !== 'function') {
    throw new Error('Type Error')
  }

  context = context || window
  const fnSymbol = Symbol()
  context[fnSymbol] = this
  const args = arguments[1]
  const result = context[fnSymbol](args)
  delete context[fnSymbol]
  return result
}
Function.prototype.myBind = function(context) {
  if (typeof this !== 'function') {
    throw new Error('Type Error')
  }
  const args = [...arguments].slice(1)
  const fn = this
  return function Fn() {
    return fn.apply(
      this instanceof Fn ? this : context,
      args.concat(...arguments)
    )
  }
}
mileOfSunshine commented 5 years ago

Class

this指向

如果将类中的方法提取出来单独使用,this 会指向该方法运行时所在的环境。

解决方法:

  1. 在构造方法中绑定 this, react里方法绑定就是这种形式。
    class Logger {
    constructor() {
    this.printName = this.printName.bind(this)
    }
    }
  2. 使用箭头函数. 箭头函数内部的 this 总是指向定义时所在的对象。
    class Obj {
    constructor() {
    this.getThis = () => this
    }
    }

静态方法

  1. 静态方法不会被实例继承,直接通过类来调用
  2. 静态方法中的 this 指向的是类,不是实例
  3. 静态方法可以与非静态方法重名
  4. 父类的静态方法可以被子类继承
  5. 静态方法也可以从 super 对象上调用

实例

  1. 实例属性除了定义在 constructor() 方法里面的 this 上面,也可以定义在类的最顶层。

私有属性( 提案 # )

  1. 私有属性和方法都可在前加入#
  2. 私有属性也可以设置getter和setter方法
  3. 私有属性不限于从 this 引用,只要是在类的内部,实例也可以引用私有属性
  4. 私有属性和私有方法前面,也可以加上 static 关键字,表示这是一个静态的私有属性或私有方法。

new.target 属性

  1. 在构造函数中调用
  2. 子类继承父类时,new.target 会返回子类
mileOfSunshine commented 5 years ago

Class的继承

super

super 既可以当作函数使用,也可以当作对象使用。

函数使用:

  1. super 代表父类的构造函数,但返回的是子类的实例,即 super 内部的 this 指的是子类的实例,super 相当于 父类.prototype.constructor.call(this)
  2. 只能在子类的构造函数之中使用
  3. 子类必须在 constructor 方法中调用 super 方法的原因: 子类自己的 this 对象必须先通过父类的构造函数完成塑造,得到与父类同样的实例属性和方法,然后再对其进行加工,加上子类自己的实例属性和方法。如果不调用 super 方法, 子类就得不到 this 对象。
  4. 在子类的构造函数中,只有调用 super 之后,才可以使用 this 关键字,否则会报错。这是因为子类实例的构建基于父类实例,只有 super 方法才能调用父类实例。

对象使用:

  1. 在普通方法中,指向父类的原型对象(父类.prototype
  2. 在静态方法中,指向父类
  3. 定义在父类实例上的方法/属性无法通过 super 调用
  4. ES6规定,在子类普通方法中通过 super 调用的父类的方法时,方法内部的 this 指向当前的子类实例。
  5. 在子类静态方法中通过 super 调用的父类方法时,方法内部的 this 指向当前的子类,而不是子类的实例
mileOfSunshine commented 3 years ago

运算符

mileOfSunshine commented 3 years ago

函数(一):属性

函数的length

length 是函数的一个属性,表示函数希望接收的命名参数的个数,即形参的个数。

(function () {}).length // 0
(function (a) {}).length // 1
(function (a, b) {}).length // 2

ES6的函数的length

(function (a) {}).length // 1

/* 指定函数默认值会造成length属性失真 */
(function (a = 5) {}).length // 0

/* length属性的返回值,等于函数的参数个数减去指定了默认值的参数个数 */
(function (a, b, c = 5) {}).length // 2

/* 如果设置了默认值的参数不是尾参数,那么length属性也不再计入后面的参数了 */
(function (a = 0, b, c) {}).length // 0
(function (a, b = 1, c) {}).length // 1

/* rest 参数也不会计入length属性 */
(function(...args) {}).length // 0
mileOfSunshine commented 2 years ago

? 类的 prototype 属性和proto属性

Class 作为构造函数的语法糖,同时有prototype属性和__proto__属性,因此同时存在两条继承链。

(1)子类的__proto__属性,表示构造函数的继承,总是指向父类。

(2)子类prototype属性的__proto__属性,表示方法的继承,总是指向父类的prototype属性。

class A {
}

class B extends A {
}

B.__proto__ === A // true
B.prototype.__proto__ === A.prototype // true

? 实例的 __proto__ 属性

var p1 = new Point(2, 3);
var p2 = new ColorPoint(2, 3, 'red');

p2.__proto__ === p1.__proto__ // false
p2.__proto__.__proto__ === p1.__proto__ // true
mileOfSunshine commented 2 years ago

ES6 - 数组

mileOfSunshine commented 2 years ago

? 事件循环机制(Event Loop)

结论:

console.log('script start')

async function async1() {
  await async2()
  console.log('async1 end')
}
async function async2() {
  console.log('async2 end')
  return Promise.resolve().then(()=>{
    console.log('async2 end1')
  })
}
async1()

setTimeout(function() {
  console.log('setTimeout')
}, 0)

new Promise(resolve => {
  console.log('Promise')
  resolve()
})
  .then(function() {
    console.log('promise1')
  })
  .then(function() {
    console.log('promise2')
  })

new Promise(resolve => {
  console.log('Promise2')
})
  .then(function() {
    console.log('promise2-1')
  })

console.log('script end')

解析:

mileOfSunshine commented 2 years ago

? 柯里化

可变参数版(采用剩余参数实现)

function curry(...args1) {
  let args = [...args1]
  const fn = function (...argv) {
    return curry.apply(this, args.concat(argv))
  }

  fn.toString = function() {
    return args.reduce((a, b) => {
      return a * b
    }, 1)
  }
  return fn
}

// 例子
console.log(curry(1)(2)(3)(4) + '')  // => 24
// 函数转字符串:会先调用 toString(),返回非原始值或是无此方法,则将调用 valueOf()
// 函数转数组:会先调用valueOf(),返回非原始值或是无此方法,则将调用 toString()
function curry(fn, arity = fn.length, ...args) {
  return arity <= args.length ? fn(...args) : curry.bind(null, fn, arity, ...args)
}
// 例子
var multi = curry((a, b, c) => a * b * c)
multi(1)(2)(3) // => 6

另一个种写法

function curry(fn) {
  const self = this
  return function curried(...args) {
    if (args.length >= fn.length) {
      return fn.apply(self, args)
    } else {
      return function(...args1) {
        return curried.apply(self, args.concat(...args1))
      }
    }
  }
}

//function add(a, b, c) {
//  return a + b + c
//}

function add(...args) {
  return args.reduce((sum, cur) => {
    return sum += cur
  })
}

const curryAdd = curry(add)
curryAdd(1)(2)(3) // => 6

? 反柯里化

Function.prototype.uncurry() {
  const self = this
  return function() {
    return Function.prototype.call.apply(self, arguments)
  }
}

function uncurry(fn) {
  return function() {
    const that = [].shift.call(arguments, 0)
    return fn.apply(that, arguments)
  }
}
mileOfSunshine commented 2 years ago

? 箭头函数 & 普通函数