一、this关键字
1.this关键字的含义
js中内置了this关键字,在不同的作用域中表示的含义不一样的
(1)全局的this
console.log(this) // window
(2)普通函数的this - 所谓普通函数,指的是直接拿函数名称调用的函数
function fn(){
console.log(this) // window
}
fn()
(3)自调用函数中的this
(function(){
console.log(this); // window
})()
(4)定时器中的this
setTimeout(function(){
console.log(this); // window
},1000)
(5)事件函数中的this
<button id="btn">按钮</button>
btn.onclick = function () {
console.log(this) // 事件的标签
}
(6)对象方法中的this
var obj = {
name: '张三',
age: 12,
eat: function () {
console.log(this); // 当前对象
}
}
obj.eat()
(7)箭头函数中的this - 上级作用域的this
(8)严格模式中普通函数的this
'use strict'
function fn(){
console.log(this) // undefined
}
fn()
2.this关键字含义的理解
普通函数,大多都属于window的方法,所以普通函数调用就是调用window的方法;事件的绑定,其实就是在给标签对象添加方法,最终调用,其实也是在调用标签对象的方法;定时器属于window的方法,所以定时器的调用其实就是在调用window的方法
其实一个函数定义好以后,还不能确定其中的this代表什么,主要是看这个函数最终是如何调用的。根据最终的调用方式,决定最终将函数当做了什么来调用的,才能决定this关键字的含义
function fn() {
console.log(this)
}
// 将fn当做事件处理函数
btn.onclick = fn
// 此时fn中的this就代表事件源btn
// 将fn当做定时器的处理函数
setTimeout(fn, 1000)
// 此时fn中的this就代表window
// 将fn当做对象的方法
var obj = {
name: '张三',
age: 12,
eat: fn
}
obj.eat()
// 此时fn中的this就代表对象obj
自调用函数 === window.函数()
全局中的this - window
(1)函数定义好还不能确定this代表什么 - 关键取决于谁调用的他
(2)箭头函数定义好就已经知道this代表什么 - 箭头函数中的this不能改
3.改变this的含义
js提供了3个函数来强行修改this关键字的含义:
3-1.call方法
call方法有两个作用:调用函数以及改变函数中的this
(1)调用函数
语法:函数.call()
(2)改变this
语法:函数.call(新的this含义)
(3)参数处理
语法:函数.call(新的this含义, 给原函数传递实参1, 给原函数传递实参2)
给call传递第一个参数为null,就表示将函数中的this改为了window
function fn() {
console.log(this);
}
fn() // window
fn.call() // window
fn.call(document) // #document
function add(a, b) {
console.log(a + b);
console.log(this);
}
add(1, 2) // 3
add.call(document, 3, 4) //7 #document
// 函数需要的实参,从call的第二个实参开始传递
3-2.apply方法
apply和call的作用是相同的,唯一不同的地方,在于传递实参。
call在调用函数时,给函数传递实参,是从call的第二个参数开始传递;apply给函数 传递实参,是将所有实参组成一个数组作为apply的第二个参数传递的:
apply方法有两个作用:调用函数以及改变函数中的this
(1)调用函数
语法:函数.apply()
(2)改变this
语法:函数.apply(新的this含义)
(3)参数处理
语法:函数.call(新的this含义, [实参1, 实参2, ...])
function fn() {
console.log(this);
}
fn.apply(document) // #document
function add(a, b) {
console.log(a + b);
console.log(this);
}
add.apply(document, [3, 4]) //7 #document
// add需要的实参,都组成一个数组,作为apply的第二个参数
(4)call和apply的使用场景
场景1:精准的检测对象的具体类型
{}有toString,转成字符串的结果是 [object Object]
toString方法中的this代表的是{}
我们希望date能使用到{}的toSting方法,结果像上面的结果一样 - 改this
{}.toString.call(date) [object Date]
// 场景1:精准的检测对象的具体类型
var date = new Date()
var arr = []
var obj = {}
Math
// 类型检测 - typeof
console.log(typeof date);
console.log(typeof arr);
console.log(typeof obj);
console.log(typeof Math);
// typeof检测对象类型时候太单一 - 不能很直观的看到对象具体是干什么用的
console.log({}.toString.call(date)); // [object Date]
console.log({}.toString.call(arr)); // [object Array]
console.log({}.toString.call(obj)); // [object Object]
console.log({}.toString.call(Math)); // [object Math]
console.log({}.toString.apply(date)); // [object Date]
console.log({}.toString.apply(arr)); // [object Array]
console.log({}.toString.apply(obj)); // [object Object]
console.log({}.toString.apply(Math)); // [object Math]
3-3.bind方法
bind方法有两个作用:复制函数,并改变新函数中的this。
(1)复制函数
语法:函数.bind() ----- 返回新函数
复制出来的新函数和原本的函数一模一样,但不是同一个
(2)改变this
语法:函数.bind(新的this含义) ------ 返回的新函数中,this的含义会变成新的含义
bind不加参数,新函数的this为window
(3)参数处理
语法:函数.bind(改变后的this含义) - 复制函数,返回一个新函数
function fn() {
console.log(this);
}
var newFn = fn.bind(document)
console.log(newFn);
newFn() // #document
console.log(fn === newFn); // false - 新函数和原函数不是同一个
function add(a, b) {
console.log(a + b);
console.log(this);
}
var newAdd = add.bind(document)
newAdd(3, 4) //7 #document
(4)bind使用场景
<body>
<button>按钮</button>
</body>
<script>
document.querySelector('button').onclick = function () {
setTimeout((function () {
// 改变btn的背景颜色
// document.querySelector('button')
this.style.backgroundColor = 'red'
}).bind(this), 500)
}
二、ES6语法
es6是ECMAScript的第6个版本,在2015年发布。也有的人将ECMAScript5之后版本,统称为es6。比起之前的语法,es6有很多新语法,让开发者在操作数据和函数的时候,功能既多,操作又简便。
1.定义变量
1-1.let
(1)let是es6新增的用于定义变量的关键字 - 使用方式跟var是一样的
var a = 10
console.log(a); // 10
let b = 20
console.log(b); // 20
(2)let定义变量没有预解析
var a = 10
console.log(a); // 10
var a
var b = 20
console.log(b); // 报错
let b
(3)let不允许重复定义变量
var a = 10
var a = 20
console.log(a); // 20
let b = 10
let b = 20
console.log(b); // 报错
(4)let定义的变量会自己创建一个块级作用域,将自己的作用域限制在大括号中。
例1
for (var a = 1; a <= 3; a++) {
} console.log(a); // 4
for (let a = 1; a <= 3; a++) {
} console.log(a); // a没定义
例2
var a = 10 // window打开有 a=10
let a = 10 // window打开没有 a=10 - 说明它自带了一个作用域,大括号外面才是真正的window
console.log(window);
例3
for (var a = 1; a <= 3; a++) {
(function (a) {
setTimeout(function () {
console.log(a);
}, a * 1000)
})(a)
}
for (let a = 1; a <= 3; a++) {
setTimeout(function () {
console.log(a);
}, a * 1000)
}
(5)暂时性死区
var a = 10
function fn() {
// 暂时性死区
a = 20
console.log(a);
// let其实还是会提前解析到,只是解析后也不能使用
let a = 30
}
fn()
console.log(a);
(6)总结:
(1)没有预解析
(2)不能重复定义
(3)会自带块级作用域(全局定义的变量不在window中)
1-2.const
(1)const是es6新增的用于定义常量的关键字 - 使用方式跟var是一样的,具备let的所有特性,另外,const定义的变量的值不能修改。
(2)const没有预解析
console.log(a); // 报错 - 没有预解析
const a = 10
(3)const不能重复定义变量
const a = 10
const a = 20
(4)const定义的变量会自己创建一个块级作用域,将自己的作用域限制在大括号中。
if (true) {
const a = 10
}
console.log(a); // 报错 - 有块级作用域
(5)const跟let不一样的地方
(5-1)定义就必须赋值
let a
a = 10
const b // 报错
b = 20
(5-2)const的值不能改
let a = 10
a = 20
console.log(a);
const b = 20
b = 30
console.log(b);
(6)总结:
(1)let具有的特性const都有
(2)定义就必须赋值
(3)值不可以改
2.箭头函数
是es6中提供的新的定义函数的语法
其实是对以前函数定义的一种简写 - 只能简写匿名函数
语法: (形参)=>{代码段} -- 匿名函数
// 平时写法
function fn() {
console.log(666);
}
var fn = function() {
console.log(666);
}
var fn = () => {
console.log(666);
}
fn()
var add = function(a, b) {
console.log(a + b);
}
var arr = (a, b) => {
console.log(a + b);
}
add(3, 4)
1.当小括号中只有一个形参的话,就可以省略小括号
var add = function (a) {
console.log(a + 6);
}
var add = (a) => {
console.log(a+6);
}
add(2)
2.当大括号中只有一行代码的话,就可以省略大括号
var add = function (a) {
console.log(a + 6);
}
var add = a => console.log(a+6);
add(2)
当箭头函数省略大括号后,如果这行代码中有return关键字,return关键字必须省略
var add = function (a) {
return a + 3
}
var add = a => a + 3 // 如果return a + 3就会报错
var sum = add(4)
console.log(sum);
3.箭头函数使用场景:通常在数组方法中使用
map - 新元素
filter - 条件
every - 条件
some - 条件
find - 条件
findIndex - 条件
以上几个数组方法的通常写法: 数组.方法(function (v) {
return 内容
})
箭头函数写法: 数组.方法(v => 内容
4.箭头函数中没有this关键字 - 可以说箭头函数中的this代表的是箭头函数所在作用域中的this
var fn = () => {
console.log(this); /* 想输出this关键字,就得从上级作用域中查找,也就是说箭头函数里
面的this代表上级作用域中的this - 此时为window */
}
fn()
document.onclick = () => {
console.log(this); // window
}
5.箭头函数定义好了就已经知道this代表什么了
var obj = {
name: '张三',
eat: () => {
console.log(this); /* window - 因为obj没有作用域,只有函数有作用域,所以直接到
全局找this */
}
}
obj.eat.call(document)// 箭头函数中的this不能修改
3.形参默认值
因为函数调用随机数时,多数情况下有一个参数为0,所以希望调用的时候能简便一点
随机颜色 - getRandom(0,256)
随机字符 - getRandom(97,123)
随机left值 - getRandom(0,innerWidth-30)
随机下标 - getRandom(0,length)
于是es6提供了一个简化语法:当需要让一个形参变成可选项,可传递实参也可以不传递的时候,es6中新语法,快速实现需求
语法:function fn(a,b= 值){}
1.函数的形参b可以给传递实参,也可以不传递
2.带有默认值的形参必须在所有形参的末尾
function getRandom(a, b= 0) { // 形参可以用默认值,可传可不传
return Math.floor(Math.random() * Math.abs(a - b)) + Math.min(a, b)
}
console.log(getRandom(5,10));
// 注意: 带有默认值的形参,必须放在所有形参的末尾
function getRandom(a=0, b) { // a=0放前面,这样不行,输出NaN
return Math.floor(Math.random() * Math.abs(a - b)) + Math.min(a, b)
}
4.模板字符串
es6提供了新的语法用于定义字符串:
var str= `字符`
1.一个字符串多行定义,在控制台就多行显示 ---- 在代码书写端怎么写的,就在控制台怎么显示
2.字符串内部通过${变量}可以直接解析变量,省略拼接
// 图片路径
var imgPath = 'a/b/c/1.jpg'
// 以前写图片链接时的操作,拼接一大堆
var img = '<img src="'+imgPath+'"/>'
// 现在用`字符`
var img=`<img src="${imgPath}">`
console.log(img)
5.解构赋值
解构赋值:是快速地方便的批量的把对象或者数组中的多个值赋值给多个变量
1.对象解构:
(1)var {属性名, 属性名, ..} = 对象
(2)var {属性名: 新的变量名} = 对象
(3)var {属性名: {属性名}} = 对象
var obj = {
name:'张三',
age:12,
isman:'是',
wife :{
name:'翠花',
age:13,
isman:'不是'
}
}
// var {name,age,isman} = obj // 1.属性名和变量名必须保持一致
// console.log(name,age,isman)
// var {name:a,age:b,isman:c}=obj // 2.取别名
// console.log(a,b,c)
// console.log(age) // 显示age未定义,取了别名后原来的名称就不能使用了
// var {wife:{name,age}} = obj // 3.多重解构
// console.log(name,age)
2.数组:
(1)var [变量名, 变量名, ..] = 数组
(2)var [变量名, [变量名]] = 数组
(3)不想要的元素可以用下划线代替
var arr = [
1,
2,
[
4,
5
],
3
]
// var [a,b,c]=arr
// console.log(a,b,c);
// var [a,b,[d,e]]=arr
// console.log(d,e)
// console.log(a,b,d,e)
// 现在只想要d,e ,不想要a,b - 用下划线作为变量名
// var [_, _, [d, e]] = arr
// console.log(d, e)
3.最简短的交换两个变量的值 - 解构赋值:
var a = 1
var b = 2
var [a, b] = [b, a]
console.log(a, b);
6.展开合并运算/...运算
1.展开:
展开运算符:...
(1)对象展开 --- ...对象 --- 将对象展开成多个键值对
(2)数组展开 --- ...数组 --- 将数组展开成多个值 --- Math.max(...数组)
// 对象展开
var obj = {
name: '隔壁老王',
age: 56,
isMan: true
}
var pbj = {
...obj,
wife: {
name: '翠花',
age: 13,
isMan: false
}
}
console.log(pbj);
// 数组展开
var arr = [1,9,5,7,3,4,6] // 想求最大值
Math.max(...arr) // arr是数组,Math.max()括号里只能放数字,所以要展开
var arr = [1,9,5,7,3,4,6] // 想求前三个元素的和
function fn(a,b,c){
console.log(a+b+c)
}
fn(...arr)
2.合并:
(1)...数组名 --- 当实参数量不固定,就将不固定数量的实参收集成一个数组
function fn(...arr){ // 将多个值收集在一个数组中
console.log(arr)
}
fn(1, 2)
fn(1, 3, 5)
fn(1, 9, 6, 4, 2, 8)
7.对象简写
1.属性:
当属性名和值使用的变量名同名就可以简写 --- var obj = {name, age}
2.方法:
当方法的值是匿名函数就可以简写 --- var obj = {eat() {}}
var name = '张三'
var age = 12
var sex = '男'
/* 想变成这样的方式
{
name: '张三',
age: 12,
sex: '男'
}
*/
// 属性简写: 当对象的属性名 和对应的值所使用的的变量名 同名了,就可以简写了
var obj = {
'name': name, // 此处为: '属性':变量
'age': age,
'sex': sex
}
console.log(obj);
// 怎么简写
var obj = {
name, // 这叫一带二,它代表键值对
age,
sex
}
console.log(obj)
// 方法简写: 当对象的方法的值是一个匿名函数的时候,方法可以简写
var obj = {
name,
age,
sex,
eat: function(){
console.log('吃吃吃')
}
}
console.log(obj)
// 怎么简写
var obj = {
name,
age,
sex,
eat(){
console.log('吃吃吃')
}
}
console.log(obj)
8.Map
这是一种es6新提供的一种对象数据,Map数据是一个键值对,类型是object --- 跟之前学习的object的区别是键可以是任意类型的数据
Map和object的区别:
object的键必须是字符串,如果给的不是字符串,会转成字符串作为对象的键;
Map的键,可以是任意数据类型
// 对象
var obj = {
name: '张三'
}
// // console.log( obj.toString() ); // 对象obj以字符串形式显示为[object,Object]
var pbj = {}
pbj[obj] = 111 // 此时obj是键,把对象obj当成对象pbj的键
console.log(pbj); // 显示{[object,Object]:111}
1.定义:
(1)var m = new Map()
(2)var m = new Map([[键, 值], [键, 值]])
2.方法:
(1)set:设置键值对
(2)get:获取值
(3)delete:删除键值对
(6)keys:把所有键组合在一起
(7)values:把所有值组合在一起
(8)forEach:遍历
3.属性:
(1)has:判断有没有某个键
(2)size:判断有几个键值对
(3)clear:清空键值对
// 操作方法
var obj = {
name: '张三'
}
var pbj = {}
var m = new Map([[obj, '值'], [pbj, '值'], ['name', '如花'], ['age', '18'], ['sex', '女']])
// 设置键值对
m.set('name', '秋香') // 覆盖如花,说明键是唯一的
console.log(m);
// // 获取值
var age = m.get('age')
console.log(age)
// 删除键值对
m.delete('sex')
console.log(m)
// 把所有键组合在一起
console.log(m.keys());
// 把所有值组合在一起
console.log(m.values());
// 遍历
// 值
m.forEach(v => {
console.log(v);
})
// 键值对
m.forEach((v, key) => {
console.log(v, key);
})
// 判断有没有某个键
console.log(m.has('wife'))
// 判断有几个键值对
console.log(m.size);
// 清除键值对
m.clear()
console.log(m);
9.Set
es6提供的一种新的对象类型的数据 - Set
官方解释:Set是所有键的集合
我们理解的:set是新型的数组,数组中不能有重复的元素,重复也只显示一个,类型是object,是没有重复值的集合
1.定义:
(1)var s = new Set()
(2)var s = new Set([多个值]) --- 多个值可以有重复,但只接受一个
var s = new Set([1,2,3,4,4,4,4,4,4,4,4]) // 重复也只显示一次
console.log(s);
2.方法:
(1)add:添加一个元素
(2)delete:删除一个元素
(3)clear:清空元素
(4)forEach:遍历
3.属性:
(1)size:获取键值对个数
4.数组去重应用:
(1)[...new Set(数组)]
// 利用set可以进行数组去重
var arr = [1,1,2,2,3,3,4]
var s = new Set(arr)
var brr = [...s] // 把s展开成一个多的值,放在brr里
console.log(s);
console.log(brr)
console.log([...new Set[arr]]) // 连在一起输出
10.for...of
es6提供的一种新的遍历语法
1.可以遍历数组/字符串/set/map,不能遍历对象
2.只遍历值,不遍历下标
// 数组
var arr = ['张三', '宛四', '宋五', '许六']
for(var a of arr){
console.log(a);
}
// 字符串
var brr = 'abcdefg'
for(var b of brr){
console.log(b);
}
// 对象 - 不能遍历对象
// var crr = {
// name:'龙崽'
// }
// for(var c of crr){
// console.log(c);
// }
// Map
var map = new Map([['a',1],['b',2]])
for(var mappp of map){
console.log(mappp);
}
// Set
var set = new Set([1,2,3,4,4,4,4,4,4,4,4])
for(var settt of set){
console.log(settt);
}