3-1
- 变量单一声明方式
- String Boolean undefined Number null
- undefined == null
- typeof(null) ‘object’
- typeof(方法) ‘function’
- typeof(+) +是运算符,不是数据类型 报错
- +0 === -0 true
- +Infinity == -Infinity false
- NaN和谁都不等
- 原始值没有属性 要打印属性、调用方法得经过基本包装类
- 包装了还没有就没办法了,比如length属性
- Boolean、Number包装了也没有length,打印就是undefined
- 可以给包装类添加值以外的属性
- 数字直接跟.有歧义,会认为是小数点
- 三种基本包装类的toString、valueOf方法各不相干+
- Function的toString、valueOf都返回函数体?
- Object的toString
- Array的toString,返回用逗号隔开
- 要打印反斜杠,单双引号必须使用字符字面量,否则报错
- 并且字符字面量还要用引号引起来
- Object Array Function Date RegExp
- 对象的属性是字符串类型
一些现象
3-2
- 目前证实会终止程序的有:语法错误、范围错误
- 0/0 NaN
- 1/0 +Infinity
- 逗号运算符在表达式中只返回最后一个
- ASCII码 0-255
3-3
- 显示类型转换,Number对null、true、false,数组友好转010
- Number对空数组和只有一个元素的数组友好
- Number对undefined、1a不友好转NaN
- ParseInt只对数字开头的友好
- null、undefined既不大于也不小于更不等于0(隐式类型转换有坑)
// 是否先对数组toString呢
console.log(Number([])) // 0
console.log(Number([1])) // 1
console.log(Number(['1'])) // 1
console.log(Number([1, 2, '3'])) // NaN
console.log(Number([1, 2, 3])) // NaN
console.log(Number({})) // NaN
3-4
- 函数声明一定要有函数名,不然报错
- 函数声明不能直接调用(无参),会报语法错误
- 函数声明function a(){}(1),这样不报错,会认为(1)是表达式不是调用
- 函数表达式可以()调用,是立即执行了
- 函数表达式,function后的函数名在外部不可见,但是.name总能获得函数名(优先取function后的,没有就取表达式声明的变量名)
- es5&&实参非undefined时形参、实参互相统一
- es6、严格模式,形参、实参不统一
- 函数是对象
3-5
- 预编译:函数声明、函数整体提升
- 变量声明,只有声明提升,赋值不提升,所以AO一开始是undefined
- -1 + NaN + ‘’ 先做-1 + NaN得到NaN 再 +’’ 得到’NaN’
3-6
- 看作用域链,函数AO、GO的this指向的是window
- AO含this、arguments和变量声明、函数声明
- 闭包是指有权访问另一个函数作用域中的变量的函数
- 立即执行函数内将闭包添加到全局,无法销毁闭包对AO的引用
- 闭包不是将被拉扯的AO放到全局!
3-7
- 表达式会忽略函数名
- 注意,此时GO里也没有b了
if (function b() { }) {console.log(typeof b) // undefined
}
console.log(b) // 报错
- 解决闭包总是拿到AO在循环中最后的值,用IIFE并实时传值进去
3-8
- 对象中的this指向对象本身
- 构造函数,用new实例化后,函数内this指向实例
- 不用new实例化,this指向window
- 当构造函数return引用值时(除了this的函数、函数声明、对象、数组、Date…),返回引用值,若是原始值则忽略
- 实例方法中的this,谁调用就指向谁
function MyTest(name) {this.name = namereturn this // 写或不写,返回的都是实例
}
console.log(new MyTest('Lee'))
3-10
- 字面量方式改变构造函数prototype(指向新的对象)若在实例化前,则实例的__proto__会指向修改后的原型,若在实例化后,实例的__proto__还是指向默认的prototype
- 若使用xx.prototype.属性 = 的修改方式(没有指向新对象),则实例访问到的属性也会跟着盖面
4-1
- 对于++ --,会计算再赋值,能让实例增添属性
- 将null作为实例的原型,实例中将不包含任何属性!,将{}作为原型仍有__proto__
- 因此,不是所有对象都继承Object.prototype
- 手动增加的__proto__和自身的不一样,不能在手动增加的_proto__向上查找原型链
- 原型链的终点是Object.prototype
4-2
- 要在构造函数(而不是原型)中定义引用类型的属性(防止所有实例共享引用值)
- 原型链实现继承(子类的原型指向父类的实例)的问题一:子类的实例可能会共享引用类型值(原本是父类构造函数中定义的属性)
- 问题二:不能传参
- 借用构造函数(子类构造函数内:父类构造函数call(this)),解决了原型链继承的问题,但函数无法复用,超类型原型中的方法,对子类型不可见
- 组合继承(原型链+借用构造函数),问题:超类的构造函数都执行了2次
- Object.create(对象,{属性:属性描述符}),第二个参数将属性添加到新对象上
- 寄生组合式继承,用Object.create()指定新对象的__proto__,用**.constructor增强对象,结合call**
- 圣杯模式实现继承:继承父类原型上的属性和方法,没有解决引用类型值问题,构造函数不能传值
4-3
- 对象遍历(for (var key in obj))原型上的属性也会打印,用hasOwnProperty区分(严谨)
- 数组遍历(for (ele val of arr))// ele是每一项arr的值
- arguments.callee - 正在被执行的函数对象(在自启动函数中使用递归,且不需要函数名)
- test.caller - test函数的调用者
for…of语句在可迭代对象(包括 Array,Map,Set,String,TypedArray,arguments 对象等等)上创建一个迭代循环,调用自定义迭代钩子,并为每个不同属性的值执行语句,可以由break, throw continue 或return终止。在这些情况下,迭代器关闭。
for…in不应该用于迭代一个关注索引顺序的 Array。
Object.keys() 方法会返回一个由一个给定对象的自身可枚举属性组成的数组,数组中属性名的排列顺序和正常循环遍历该对象时返回的顺序一致 。
在ES5里,如果此方法的参数不是对象(而是一个原始值),那么它会抛出 TypeError。在ES2015中,非对象的参数将被强制转换为一个对象。
4-4
- 手写深拷贝,考虑原始值和引用值(区分数组和对象),结合递归
4-5
- new Array()的参数用于设置数组长度,或设置数组(只传一个非数字值时)
- 数组方法 - 继承了数组原型上的方法
- push、unshift(返回值:执行了方法以后数组长度,可以传多个参数)
- pop、shift(返回剪切掉的那个值)
- splice(在哪个下标前添加,返回值空数组)
- 数组中认为最后一个元素的index是-1
- sort是按照ASCII码排列的(未传参时)
- sort(function(a,b){return 负数})
4-6
- concat 返回值是拼接后的数组
- slice[a,b)
- join不传参数相当于toString!用逗号连接
- split(a,b) 不传值,会将整个字符串放进数组;第二个参数b,是用a分割后截取b位
- 类数组没有数组方法,因为没有继承Array.prototype,继承Object.prototype,类数组
- 对象转类数组:给对象加splice属性,指向Array原型上的splice方法,对象的原型依然是Object.prototype (有什么意义)
- arguments对象的constructor是Object
- obj实例调用push方法的原理,和length相关(并不一定是类数组)
- length属性决定了类数组的长度,push开始的位置
- 类数组转数组:Array.prototype.slice.call(arguments)、Array.from() (分别是ES5\ES6)
- 箭头函数没有arguments
4-8
- finally一定执行
- throw要写在可能会出错的代码段前, catch(e) 可以抛出自定义信息e
- JSON.parse()参数不能是空字符串或undefined,必须是JSON字符串
- with可以改变作用域(因此会消耗性能)
严格模式下不能不写var
严格模式下,函数内部fn使用this指向undefined,但new fn实例化返回的this还是指向实例
严格模式下,函数参数不能重复
严格模式下,对象的属性不能重复
严格模式下,eval有自己的作用域,内部用var声明的变量不再是全局的
Duplicate property name in object literal is allowed in ES6 strict mode
常识归类
- 1不是质数 2是
- 质数是只能被1和自己整除的数
- 斐波那契数列,前2项是1,之后每项为前两项之和
- ASCII码charCode范围在0-255,每个字符1字节,从256开始的内容每个2字节
- 闰年(整除4&&不能整除100 || 整除400)
关于this
- 在函数内部,this的值取决于函数被调用的方式。this值没被设置时(未使用call、apply)默认指向window
- call、apply传null 和 undefined 会被转换为全局对象。原始值如 7 或 ‘foo’ 会使用相应构造函数转换为对象。
- bind的this绑定只生效一次:this将永久地被绑定到了bind的第一个参数
- 当函数作为对象里的方法被调用时,this 被设置为调用该函数的对象
- 当一个函数用作构造函数时(使用new关键字),它的this被绑定到正在构造的新对象。
- Object.assign是浅拷贝,JSON.parse(JSON.stringify)是深拷贝
var obj = {count: {a: 1,b: 2}
}
var obj1 = Object.assign({}, obj)
obj1.count.a = 100
console.log(obj.a) // 100
// 下面的例子令人迷惑,非常危险,没有用处。避免使用它们。
typeof new Boolean(true) === ‘object’;
typeof new Number(1) === ‘object’;
typeof new String(‘abc’) === ‘object’;
关于valueOf
JavaScript调用valueOf方法将对象转换为原始值。
默认情况下,valueOf方法由Object后面的每个对象继承。 每个内置的核心对象都会覆盖此方法以返回适当的值。如果对象没有原始值,则valueOf将返回对象本身。
JavaScript的许多内置对象都重写了该函数,以实现更适合自身的功能需要。因此,不同类型对象的valueOf()方法的返回值和返回值类型均可能不同。
- 控制台为什么有这样的迷惑行为…因为自己不小心重写了Object原型上的valueOf方法 = =,以下反面教材不要看
- 但是为什么Array、Function也受影响呢?原因是他们都是同一个valueOf
The Array prototype object does not have a valueOf property of its own;however,it inherits the valueOf property from the standard built-in Object property Object.
The Function prototype object does not have a valueOf property of its own; however, it inherits the valueOf property from the Object prototype Object.
关于隐式类型转换
关系操作符(< > ≥ ≤)
- 数字比较比数字
- 字符串比较比字符编码值(大写字母<小写字母)
- 有一个数字,另一个也转数字(null转0,undefined转NaN,除了和0不能比,null和非0能比较)
- 有一个布尔值,把它转为数字
- 有一个对象,调用valueOf,没有则调用toString
相等和不相等(== !=)
- 有一个布尔值,把它转为数字
- 字符串和数字,先将字符串转数字
- 有一个对象,调用valueOf得到基本类型值再走以上规则(怎么得到基本类型值,这里有文章)
- null、undefined在比较相等性前不得转换(所以和任何比较==都是 false)
- 都是对象,比较是不是相同指向
- 关系、相等运算符掺杂了其他运算符,有优先级
根据运算符优先级 ,! 的优先级是大于 == 的
[] == true // false
[] == ![] // true
{} == !{} // false
console.log({ a: 1 } == '[object Object]') // true
console.log({ a: 1 } == true) // false
console.log([] == '[object Array]') // false 数组toString是空字符串
console.log([] == '') // true
console.log([] == 0) // true
console.log([] == false) // true
const number1 = new Number(3);
const number2 = new Number(3);
number1 == 3; // true
number1 == number2; // false
// [] == ! [] -> [] == false -> [] == 0 -> '' == 0 -> 0 == 0 -> true
ECMA类型转换