根据B站的小野森森老师的js课程整理的笔记
JS发展史
五大浏览器内核
浏览器的历史和JS诞生
ECMA
编程语言
Javascript
- ECMAscript
- DOM
- BOM
JS的值
循环
类型转换
typeof
typeof返回的值都是字符串
显示类型转换
Number
字符串都是NaN
parseInt
转化为整型
true,null,undefined,NaN都是一样的结果:
第二个参数:
parseFloat
string
Boolean
undefined null NaN "" 0 :false 其他的都是true
隐式类型转换
undefined和null既不大于0也不小于0也不等于0
但是
isNaN
TRUE FALSE FALSE TRUE FALSE TRUE
函数
函数属性
length:
prototype: 是不可枚举的,因此不可以用for-in发现
实参形参
执行:
test(1)
函数参数默认值
递归
预编译
暗示全局变量
AO - activation object 活跃对象, 函数上下文
执行结果:
案例分析:
执行结果: 输出function
GO- global object 全局上下文
案例一:
结果: 1 6 6
案例二:
结果输出2
案例三:
2 undefined undefined 4
作用域链
在函数被定义的时候生成该函数的作用域链,在函数执行前一刻生成自己的AO
分析函数
a函数被定义时
a函数被执行前一刻
b函数被定义时
b函数被执行前一刻
b函数执行结束后
a函数执行结束时
闭包基础
定义
当内部函数被返回到外部并保存时,一定会产生闭包,闭包会产生原来的作用域链不释放,过度的闭包可能会导致内存泄漏,或加载过慢。
分析函数
例子:闭包返回两个函数
例一
例二
例子:闭包返回对象
立即执行函数IIFE
自动执行,执行完成以后立即释放
IIFE : immediately-invoked function expression
写法
//1.括号里面包裹的,不管是什么,最终都会变成表达式
//2.立即执行函数不需要函数名,写不写都一样,因为是立即执行(表达式忽略函数名)
(function() {...})()
(function() {...}()) //W3C建议
//函数声明变成表达式的方法: + - ! || &&
1 && function test() {
console.log(1)
}()
立即执行函数通常在前面加分号防止连起来会报错
面试题
注意: 这里第二个函数括号被认为是一个表达式,所以第二个函数不会执行
逗号运算符
经典案例
下面这段代码的运行结果是什么?
分析:
解决方法一: 使用立即执行函数
解决方法二: 使用外部传参
解决方法三: 使用立即函数传参(重要)
每次i值不一样,立即执行函数所得到的参数j也就不一样,所以每次都是使用不同的j值的函数传给数组.
面试题1
这样的结果是点击每个i都是打印5
解决办法: 使用立即执行函数
面试题2
解析:
- if的括号里面是一个函数,所以肯定不是false(null,undefined,0,false),和函数是否为空函数无关,所以if里面的语句执行
- (function b() {}) 函数声明外部有括号,所以变成了表达式,表达式内部忽略函数名,所以b就被忽略了,typeof(b)就为undefined
- a = 10 + undefined之后就变成了'10undefined'字符串
传参
(function(a,b) {
console.log(a+b)
}(3,5))
获取返回值
var x = (function(a,b) {
return a + b
}(3,5))
题目:
对象
删除对象方法:
创建对象
自定义构造函数
规范写法:
类比于vue实例
构造函数
构造函数的this指向
原理剖析
不用new的等效写法
修改构造函数
包装类
new Number
new String
注意
这两个都会报错,因为undefined和null不可以设置任何的属性和方法。
js包装类
但是下面这个为什么不报错呢?
解答:
字符串是原始值,没有属性和方法。通过str.length系统也创建了一个String对象并将length赋值3。但是new String()里面自带了一个le ngth属性。所以可以通过包装类来访问到String里面独有的length属性。
实际上是console.log(new String(str).length)
数组的截断方法
length赋值为多少,就取前几位,剩下的截断。
如果length赋值为6,则增加一位empty
那么对于字符串呢?
也是因为包装类.
面试题
题目一:
答案:
题目二:
答案:
题目三:
答案:
题目四:
哪个能输出1 2 3 4 5?
答案:
所以是第一个和第三个执行了
ASCII码和UNICODE码
Unicode码0-255位跟ASCII码一样,每个占一个字节,255位以后每个占2个字节.
应用:
答案:
题目
写一个构造函数,接受的参数数量不限,要求构造函数有两个方法,一是对输入的参数进行相加,二是对输入的参数进行相乘操作.
写法一:
写法二:
原型
prototype与_proto__
原理
特点
原型重写
window与return
插件的常用写法:
例子:
原型链
原型链的顶端是Object.prototype,它里面保存了一个toString方法
笔试题
题目一:
答案:
题目二:
答案:
即给student本身添加一个students属性,并把原型上该属性的值加1并且赋值给它.
所以结果是:
题目三:
解析:
如果是Car.prototype.intro(),则输出mazda
Object.create()
所以不是所有的对象都继承于Object.prototype
undefined和null能不能使用toString()
undefined和null既没有包装类也没有原型,所以没有toString方法,也没有相应的属性.
Object.prototype.toString与Number.prototype.toString
call和apply
应用:
继承
圣杯模式
代码:
结果:
圣杯模式的封装
作业
答案:
面试题
解析:
Foo.getName(); -> 2
把Foo这个函数当成一个对象,然后借用了这个对象的getName函数并执行,与Foo函数内部没有关系.
getName(); -> 4
预编译问题,先变量声明,然后函数声明,最后运行. getName最后运行的时候被赋值,覆盖了前面的函数.
Foo().getName(); -> 1
先执行Foo()函数,Foo内部getName之前没有var,所以当成全局的getName来处理,全局的getName这时又被Foo中的getName覆盖.然后执行this.getName(),this是window,就是log(1)了
getName(); -> 1
全局的getName函数在上一步中被Foo中的getName覆盖,所以还是log(1)
new Foo.getName(); -> 2
Foo后面没有执行符号,这时点的优先级比new高,执行Foo.getName(),所以是2. new 2 没有意义.
new Foo().getName(); -> 3
Foo后面有个执行符号,先执行 new Foo(),创建了一个实例,最后执行实例的getName函数,但是这个实例本身没有getName函数(因为Foo里面没有this.getName=...),而实例的原型上有getName函数,所以log(3)
new new Foo().getName(); -> 3
相当于new 3
对象相关
链式调用
原理
结果
对象属性遍历
for...in
hasOwnProperty
结果是: Benz red 3.0
判断属性是否存在
instanceof
判断A对象是否是构造函数B实例化出来的: A instanceof B
判断数组的方法: 三种
建议使用的方法:
typeof返回的值:6种
注意: typeof返回的值都是string类型
object(null),boolean,number,string,undefined,function
this指向
全局的this -> window
预编译函数内部的this -> window
构造函数的this -> 实例
call和apply
callee和caller
argument.callee
返回实参所对应的正在执行的函数
结果: 3 3 2
应用:自执行函数
caller
isNaN 内部原理
笔试题
题目一:
解析:
题目二:
深拷贝
方法一:
function deepClone(origin,target) {
var tar = target || {},
toStr = Object.prototype.toString,
arrType = '[Object Array]';
for (var key in origin) {
//剔除原型上的属性
if (origin.hasOwnProperty(key)) {
//判断是否为引用类型
if (typeof(origin[key]) === 'object' && origin[key] !== null) {
//判断你是否为数组
toStr.call(origin[key]) === arrType ? tar[key] = []
: tar[key] = {};
deepClone(origin[key],tar[key]);
} else {
tar[key] = origin[key];
}
}
}
return tar
}
方法二:使用json
var str = JSON.stringify(origin)
var target = JSON.parse(str)
数组
底层
数组实际上就是对象的另外一种形式:
稀松数组
如果逗号后的空值为最后一位,那么这一位无效,即不存在.
用构造函数构造数组,不能有逗号之间的空值,否则会报错.
用构造函数构造数组,若只填一个数字,则代表数组长度
方法(修改原数组)
push unshift
手写push:
pop shift
reverse
翻转
splice
sort
随机排序
方法(不修改原数组)
concat
toString
slice
复制数组:
截取: [start, end) 前面闭区间,后面开区间
join
不填相当于toString
填了就是放入分隔符
split
不放参数的话,把整个字符串当成一个元素放入数组
放参数就是根据分隔符放入数组
第二个参数就是截取的位数
重写unshift
使用splice
使用concat
数组按照元素的字节数排序
数组去重
类数组
是类似于数组的对象,但是没有继承数组的原型,即没有数组的方法.直接继承的是对象的原型:
用对象模拟类数组:
类数组转化为数组
push原理
面试题
解析:
应用:
封装一个typeof方法
提示:
答案:
tip:
typeof里面放一个未声明的变量输出undefined 不报错
JS错误信息类型
SyntaxError 语法错误
ReferenceError 引用错误
RangeError 范围错误
TypeError 类型错误
URIError URI错误
知识普及
错误
EvalError eval函数执行错误
表现
JSON数据
eval用法(不推荐使用)
es6摒弃
用法不规范(见表现) 不好调试 性能问题 网络安全
将json的字符串数据转化为可循环的对象
总结
以上6种错误都可以用户自定义并抛出,即都有对应的实例化函数
还有一个总的error
手动抛出错误
try...catch
finally
throw
扔出错误信息
ES5严格模式
ESMAScript历程
启动严格模式
第一种:
最上面一行
第二种: 函数内部
实例
with函数
严格模式规则
caller callee
a = 1 或者 a =b =1
this指向
this要赋值,不然指向undefined
函数的参数不能重复
对象的属性重复的不会显示,但不会报错
eval
严格模式下,eval有自己的作用域;非严格模式,eval是全局的作用域
垃圾回收
原理
- 找出不再使用的变量
- 释放其占用内存
- 固定的时间间隔运行
方式
标记清除
排除闭包里的变量和全局变量