js8

三种获取对象原型的方法

  • Constructor.prototype是构造函数指向原型对象的方法
  • Object.getPrototypeOf(obj)是ES5中用来获取obj对象的原型对象的标准方法。
  • obj.__proto__是获取obj对象的原型对象的非标准方法。

实现Instanceof

instanceof运算符用于判断构造函数的prototype属性是否出现在对象的原型链中的任何位置

1
2
3
4
5
6
7
8
9
10
11
12
13
function myInstanceof(left, right){
// 获取对象的原型
let proto = Object.getPrototypeOf(left)
// 获取构造函数对象的原型
let prototype = right.prototype

while(true){
if(!proto) return false
if(proto === prototype) return true
// 如果没有找到, 就继续从其原型上找
proto = Object.getPrototypeOf(proto)
}
}

解决0.1+0.2 !== 0.3

1
2
3
4
5
function numberEpsilon(arg1, arg2){
return Math.abs(arg1 - arg2) < Number.EPSILON
}
console.log(numberEpsilon(0.1 + 0.2, 0.3)); // true
console.log(0.1 + 0.2 === 0.3); //false

js包装类型

js中基本类型是没有属性和方法的,为了方便操作基本类型,在调用基本类型时js会在后台隐式将基本类型转换为对象

let、const、var区别

(1) 块级作用域由{}包括, let和const具有块级作用域, var不存在块级作用域,解决了两个问题

内层变量可以覆盖外层变量

用来计数的循环变量泄漏为全局变量

var存在变量提升, let, const不存在变量提升, 变量只能在声明后使用

var会给全局添加属性

var允许重复声明, const, let不允许

给let, const命令声明变量之前, 该变量都是不可用的, 语法上成为暂时性死区, var声明不存在

var, let可以不用设置初始值, const必须设置初始值

let可以改变指针指向, const不可以改变指针指向

const属性修改问题

const保证变量指向的地址不可改变, 对于基本的数据类型(数值, 字符串, 布尔值), 其值就保存在变量指向的那个内存地址中, 等同与常量

对于引用类型来说(对象, 数组), 变量指向数据的内存地址, 保存的只是一个指针, const只能保证这个指针固定不动, 数据结构内部不可控

箭头函数

箭头函数时es6提出的, 它没有prototype, 也没有自己的指向, 所以不可以new一个箭头函数

new操作符的实现步骤如下:

1、创建一个对象

2、将对象的proto属性指向构造函数的prototype

3、构造函数肚饿this指向该对象,为这个对象挂载属性和方法

4、返回新的对象

上面的二、三步箭头函数无法执行

箭头函数和普通函数区别

箭头函数比普通函数更简洁

如果不需要返回值, 且只有一句话, 可以给语句前面加一个void, 再调用一个函数

let fn = () => void fun()

箭头函数没有自己的this

箭头函数不会创建自己的this, 它继承了自己作用域上一层的this

箭头函数指向的this不会改变

1
2
3
4
5
6
7
8
9
10
11
12
13
14
var id = 'GLOBAL';
var obj = {
id: 'OBJ',
a: function(){
console.log(this.id)
},
b: () => {
console.log(this.id)
}
};
obj.a() // 'OBJ'
obj.b(); // 'GLOBAL'
new obj.a() // undefine
new obj.b() // Uncaught TypeError: obj.b is..

对象obj的方法b是使用箭头函数定义的, 这个函数中this就永远指向它定义时所处的全局执行环境this, 即便这个函数时作为对象obj的方法调用, this依旧指向window对象 定义对象的大括号{}无法形成一个单独的执行环境, 它依然处于全局环境中

call() bind() apply() 等方法不能改变箭头函数this指向

1
2
3
4
5
6
7
8
var id = 'Global'
let fun1 = () => {
console.log(this.id)
}
fun1(); // 'Global'
fun1.call({id: 'obj'}) // 'Global'
fun1.apply({id: 'obj'}) // 'Global'
fun1.bind({id: 'obj'})() // 'Global'

箭头函数不能作为构造函数使用

没有arguments

扩展运算符

1.对象扩展运算符

对象的扩展运算符(…)用于取出参数对象中的所有可遍历属性, 拷贝到当前对象中

1
2
let bar = {a: 1, b: 2}
let baz = {...bar} // {a: 1, b: 2}

上述方法等价于

1
2
let bar = {a: 1, b: 2}
let baz = Object.assign({}, bar) //

Object.assign()方法用于对象的合并, 将源对象source的所有可枚举属性, 复制到目标对象(target)

Object.assign方法第一个参数是目标对象, 后面参数是源对象(如果有同名属性, 后面会覆盖前面)

1
2
let bar = {a: 1, b: 2}
let baz = {...bar, ...{a: 2, b: 4}} //被覆盖

2.数组扩展运算符

1
2
3
console.log(...[1,2,3]) // 1 2 3
console.log(...[1, [2, 3, 4], 5])
// 1 [2, 3, 4] 5

数组扩展运算符的应用

1
2
3
4
5
function add(x, y){
return x + y
}
const numbers = [1, 2]
add(...numbers) // 3

复制数组

1
2
const arr1 = [1,2]
const arr2 = [...arr1]

扩展运算符会取出参数对象中所有可用属性, 拷贝到当前对象中

合并数组

1
2
3
const arr1 = ['two', 'three'];
const arr2 = ['one', ...arr1, 'four', 'five']
// ['one', 'two', 'three', 'four', 'five']

将字符串转换为真正的数组

1
[...'hello']

对数组和对象的结构

解构是ES6提供的新的提取数据的模式,这种模式能够从对象或数组中针对性拿到想要的东西

数组解构

1
const [a,b,c] = [1,2,3]

或者留空

1
const [a,,c] = [1,2,3] // a: 1, c: 3

对象结构

解构对象是以属性名称为匹配条件

1
2
3
4
const stu = {
name: 'Bob',
age: 24
}

解构属性

1
2
const {name, age} = stu	//name: 'Bob' age: 24
// 调换位置也一样

new操作符实现(手写)

1.首先创建了一个新的空对象

2.设置原型, 将对象的原型设置为函数的prototype对象

3.让函数的this指向这个对象, 指向构造函数的代码

4.返回对象

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
function Dog(name){
this.name = name
}
Dog.prototype.sayName = function (){
console.log(this.name)
}

function _new(fn, ...args){
// 先用object创建一个新对象
const obj = Object.create(fn.prototype)
// obj指向构造函数原型了, 再修改this指针
const res = fn.apply(obj, args)
return res instanceof Object ? res : obj
}
let dog = _new(Dog, '大毛')
dog.sayName()