1.类的本质
class(类)的本质是function,可以简单的认为 类是构造函数的另一种写法
- 类有原型对象
prototype
- 类的原型对象
prototype
中有constructor
指向类本身 - 类可以通过原型对象添加方法
- 类创建的实例对象有
__proto__
,指向类的原型对象
ES6的类的绝大部分功能ES5都能做到,新的class
写法只是让对象原型的写法更清晰、更像面向对象编程的语法而已
ES6的类就是语法糖
语法糖:是指一种便捷方法,简单理解,有两种方法可以实现同样的功能,但一种写法更加清晰、方便,那这个方法就是语法糖
2. ES5新增方法
- 数组方法
迭代(遍历)方法:forEach()
、map()
、filter()
、some()
、every()
forEach()
语法: array.forEach(function (currentValue, index, arr){})
currentValue
数组当前项的值
index
数组当前项的索引
arr
数组对象本身
// 求和示例
var arr = [1, 2, 3]
var sum = 0
arr.forEach(function (val, index, arr) {
sum += val
})
console.log(sum);
filter()
语法:array.filter(function(currentValue, index, arr){})
currentValue
数组当前项的值
index
数组当前项的索引
arr
数组对象本身
创建一个新数组,新数组中的元素是通过检查指定数组中符合条件的所有元素,主要用于筛选数组
直接返回一个新数组
// 筛选数组元素示例
var arr = [66, 5, 12, 7, 29]
// 因为filter返回一个新数组,所以可以直接先声明变量去接收
var newArr = arr.filter(function (val, index, arr) {
// return val >= 12 // 筛选>=12的元素
// 筛选偶数值 (%-->取余)
return val % 2 === 0
})
console.log(newArr);
some()
语法: array.some(function (currentValue, index, arr) {})
currentValue
数组当前项的值
index
数组当前项的索引
arr
数组对象本身
用于检测数组中的元素是否满足指定条件,及查找数组中是否有满足条件的元素
返回布尔值,如果能找到就为true,找不到返回false
如果找到第一个满足条件的元素,则终止循环,不再继续查找
var arr = ['red', 'pink', 'blue']
var flag = arr.some(function (val, index, arr) {
return val === 'pink'
})
console.log(flag);
forEach和some的区别
forEach(filter也同样) 会一直遍历,即使有return也不会终止迭代
some在return true 时会停止遍历,效率更高
当想要在数组中查询唯一元素,用some更合适
- 字符串方法
trim()
从一个字符串两端删除空白字符
语法:str.trim()
不影响原字符串本身,返回一个新字符串
var str = ' 星星 '
var newStr = str.trim()
console.log(newStr);
- 对象方法
object.defineProperty()
定义对象中的新属性或修改原有属性
语法:Object.defineProperty(obj, prop, descriptor)
obj
:必需。目标对象
prop
:必需。需定义或修改的属性的名字
discriptor
:必需。目标属性所拥有的特性
discriptor
说明:以对象形式{}
书写
value
设置属性的值,默认为undefined
,不写的情况下为对象中的默认值
writable
值是否可重写,true|false
,默认值为false
enumerable
目标属性是否可以被枚举,true|false
,默认值为false
configurable
目标属性是否可以被删除或是否可以再次修改特性(该属性的特性(即 configurable的值)),true|false
,默认值为false
var obj = {
id: 1,
name: '小米',
price: 9999
}
Object.defineProperty(obj, 'address', {
value: '北京',
// writable 值为false 则不允许修改这个属性值 默认值为false
writable: false,
// enumerable 值为false则不允许遍历 默认值为false
enumerable: false,
// configurable 如果值为false 则不允许删除这个属性 也不允许修改该属性的特性(即 configurable的值) 默认为false
configurable: false
})
// 遍历对象
console.log(Object.keys(obj));
// 删除对象中的属性
delete obj.address
console.log(obj);
3. 函数进阶
- 函数的定义和调用
定义方式
1>函数声明方式function
关键字(命名函数)
function fn () { }
2>函数表达式(匿名函数)
var fn = function () { }
3>new Function方式
Function中的参数必须是字符串格式
new Function('参数1', '参数2'..., '函数体')
var f = new Function('console.log(123)'); f();
var f = new Function('a', 'b', 'console.log(a+b)'); f(1, 2);
所有函数都是Function
的实例(对象)
函数也属于对象(函数、字符串、数组。。。万物皆对象)
调用方式
1>普通函数
function fn () {console.log(123)}
fn() | fn.call()
2>对象的方法
var o = {
sayHi: function () {
console.log(123)
}
}
o.sayHi()
3>构造函数
function Star () {}
Star()
4>绑定事件函数
btn.onclick = function () {}
点击按钮就可以调用
5>定时器函数
setInterval(function () {}, 1000)
每隔1s调用一次
6>立即执行函数
// 立即执行函数是自动调用
(function () {
console.log(123)
})()
- this
this的指向在函数调用时确定,调用方式不同,决定了this指向不同,一般指向调用者
处理函数内部this指向的方法
1> call()
调用一个对象,即调用函数的方式,以及改变函数的this指向
主要作用可以实现继承
fun.call(thisArg, arg1, arg2, ...)
var o = { name: 'andy' }
function fn (a, b) {
console.log(this)
console.log(a + b)
}
fn.call(o, 1, 2)
// o->this指向
2> apply()
调用函数且改变this指向
fun.apply(thisArg, [argsArray])
thisArg
在函数运行时指定的this值
argsArray
传递的参数必须是**数组(伪数组)**形式
返回值就是函数的返回值,因为它就是调用函数
var o = { name: 'andy' }
function fn (arr) {
console.log(this)
console.log(arr) // 暂未成功人士
}
fn.apply(o, ['暂未成功人士'])
主要应用:
如,可以利用apply
借助于数学内置对象求最大值
var arr = [1, 66, 5, 99]
var max = Math.max.apply(Math, arr)
console.log(max)
3> bind()
不会调用函数,但是能改变函数内部的this
指向
fun.bind(thisArg, arg1, arg2,...)
thisArg
在函数运行时指定的this值(this指向)
arg1, arg2
传递的其他参数
返回由指定的this值和初始化参数改造的原函数拷贝(原函数改变this之后产生的新函数)
var o = { name: 'andy' }
function fn () {
console.log(this)
}
var f = fn.find(o)
f()
如果有的函数不需要立即调用,但是又想改变函数内部的this指向时,用bind方法
应用
一个按钮,点击之后禁用,3s后开启
<button>点击</button>
var btn = document.querySelector('button')
btn.onclick = function () {
this.disabled = true
// var that = this
setTimeout(function () {
// that.disabled = false // 定时器函数中的this指向window
this.disabled = false
}.bind(this), 3000)
}
如果同时需要用到本身的this和改变指向的this时,可以先传递当前的this,再传递要修改为的this指向
this.remove[i].onclick = this.delTab.bind(this.remove[i], this)
- 严格模式
在严格条件下,执行js代码
严格模式在IE10以上版本的浏览器中才会被支持,旧版本浏览器中会被忽略
严格模式对正常js的语义做了一些更改:
1> 消除js语法的一些不合理、不严谨之处,减少了一些怪异行为
2> 消除代码运行的一些不安全之处,保证代码运行的安全
3> 提高编译器效率,增加运行速度
4> 禁用了ECMAScript未来版本可能会定义的语法,为未来新版本的JavaScript做好铺垫,如一些保留字:class
、enum
、export
、extends
、import
、super
不能做变量名
严格模式可以 应用到整个脚本或个别函数中,使用时,可以分为为脚本开启严格模式和为函数开启严格模式
1> 为脚本开启严格模式
在所有语句之前放一个特定语句"use strict";
或'use strict';
<script>
'use strict';
// 下面的代码会按照严格模式执行
</script>
// 或 使用立即执行函数时 开启严格模式
<script>
(function () {
'use strict';
})()
</script>
2> 为某个函数开启严格模式
// 此时只为 fn 函数开启了严格模式
function fn () {
'use strict';
}
严格模式中的变化
1> 变量规定
正常模式中,如果变量没声明就赋值,默认是全局变量;严格模式禁止这种用法,变量必须都先用var
命令声明,然后再使用
严禁删除已声明变量。例,delete x;
语法是错误的
2> this 指向
全局作用域函数中的this
指向window
对象
严格模式下全局作用域中的函数的this
是undefined
正常模式中构造函数不加new
也可以作为普通函数调用,this
指向全局
严格模式下,如果构造函数不加new
调用,this
指向undefined
,如果给它赋值,会报错
new
实例化的构造函数指向创建的对象实例
定时器中的this
还指向window
事件、对象中的this
还指向调用者
3> 函数变化
函数不能有重名的参数
函数必须声明在顶层,且不允许在非函数的代码块内声明函数
'use strict';
if (true) {
function f () {} // 语法错误
f()
}
for (var i = 0; i < 5; i++) {
function f2 () {} // 语法错误
f2()
}
function f3 () { // 合法
function f4 () {} // 合法
}
- 高阶函数
高阶函数是对其他函数进行操作的函数,它接收函数作为参数或将函数作为返回值输出
// 作为参数
function fn (callback) {
callback && callback() // 如果存在callback 则执行
}
fn(function () {alert('hi')})
// 作为返回值
function fn () {
return function () {}
}
fn()
此时fn就是高阶函数,函数也是一种数据类型,同样可以作为参数,传递给另一个参数使用。最典型的是作为回调函数
- 闭包
闭包(closure)指有权访问另一个函数作用域中变量的函数。即,一个作用域访问另外一个函数内部的局部变量,就产生了闭包(被访问的变量所在的函数就是闭包函数)
function fn () {
var num = 10
function fun () {
console.log(num)
}
fun()
}
fn()
// 在fn外的作用域可以访问fn内部的局部变量
function fn () {
var num = 10
// function fun () {
// console.log(num)
// }
// return fun()
// 另一种写法
return function () {
console.log(num)
}
}
var f = fn()
f()
//类似于
// var f = function fun () {
// console.log(num)
// }
闭包的主要作用:延伸了变量的作用范围
- 递归
4. 递归
如果一个函数在内部可以调用其本身,这个函数就是递归函数(函数内部自己调用自己)
递归的作用和循环效果一样
递归很容易发生“栈溢出”错误,所以必须加退出条件return
5. 浅拷贝和深拷贝
浅拷贝只是拷贝一层,更深层次对象级别的只拷贝了其引用(地址),此时数据指向同一个地址,修改任意一项的数据,另一项也都会改变
- es6新增浅拷贝方法
Object.assign(target, ...sources)
例Object.assign(o, obj)
即,把obj拷贝给o
var obj = {
id: 1,
name: 'andy',
msg: {
age: 18
}
}
// 浅拷贝
// 修改o[msg]的值,obj中的值也会改变
var o = {}
for (var k in obj) {
// k是属性名,obj[k]是属性值
o[k] = obj[k]
}
o.msg.age = 20
console.log(obj.msg.age) // 20
深拷贝拷贝多层,每一级别的数据都会拷贝
var obj = {
id: 1,
name: '罗飞',
msg: {
age: 18
},
friend: [
'王子麒',
'王剑'
]
}
var o = {}
// instanceof 运算符用于检测构造函数的 prototype 属性是否出现在某个实例对象的原型链上。
function deepCopy (newObj, oldObj) {
for (var k in oldObj) {
// 判断属性值属于哪种数据类型
// 1. 获取属性值
var item = oldObj[k]
// 2. 判断是否为数组
// 数组也属于object,如果先判断是否为object,会把数组当对象解析,就不会再判断array了
if (item instanceof Array) {
newObj[k] = []
deepCopy(newObj[k], item)
} else if (item instanceof Object) {
// 3. 判断是否为对象
newObj[k] = {}
deepCopy(newObj[k], item)
} else {
// 4. 判断是否为普通数据类型
newObj[k] = item
}
}
}
deepCopy(o, obj)
console.log(o);
o.msg.age = 24
console.log(obj);
深拷贝是把内存空间中的数据复制了一份给新拷贝的项,所以修改任意都不会有影响