一、this
this是函数中都有的一个变量,因为this的值在书写的时候无法确定,只有在函数被调用的时候才能确定。
function fn() {
// 这里只是书写了this,但是无法确定this 的值,只有等到函数调用的时候才能确定
console.log(this)
}
- 1.如果函数直接在全局作用域调用, 那么他的 this 就指向 window
fn() // window
- 2.将 fn 函数放在一个对象中, 然后调用;此时内部的this 就指向了这个对象
var obj = {
name: '当你看到我的时候, 证明你看到了 obj 对象',
cb: fn
}
console.log(fn()) // window
console.log(fn) // undefined
console.log(this) // ƒ
console.log(obj.cb) // ƒ
obj.cb() // {cb: ƒ}
-
- 将 函数 当成一个事件处理函数使用;此时 this 会指向事件源
var box = document.querySelector('div')
box.onclick = fn // <div>div的一个标识</div>
-
- 将函数放在定时器中执行;此时this指向window
setTimeout(fn, 0) // window
setInterval(fn, 10) // window
- 5.自执行函数;此时内部的this指向window
- 语法:
(书写要自执行的函数)(这里可以书写要传递给内部函数的实参)
(fn)() // 指向window
二、更改this
function fn(num) {
console.log(num, this)
}
var obj = {
name: '我是 obj 对象'
}
1.call
- 在 JS中,每一个函数都自带一些方法,其中一个就是 call
- 该方法是附加在函数调用后面使用, 可以忽略函数本身的 this 指向
- 语法:
函数名.call('将函数内部的this更改为谁?', '传递给函数的实参1', '传递给函数的实参2', '传递给函数的实参3' .......)
fn.call(obj, 10010) // 10010 { "name": "我是 obj 对象" }
2.apply
- 该方法是附加在函数调用后面使用, 可以忽略函数本身的 this 指向\
- 语法:
函数名.apply(要改变的 this 指向, [要给函数传递的参数1, 要给函数传递的参数2, ...])
fn.apply(obj, [12345]) // 12345 { "name": "我是 obj 对象" }
3. bind
- 和 call / apply 有一些不一样, 就是 bind 不会立即执行函数, 而是返回一个已经改变了 this 指向的函数
- 语法:
var newFn = 函数名.bind(要改变的 this 指向); newFn(传递参数)
var res = fn.bind(obj, 'bind')
res() // bind { "name": "我是 obj 对象" }
三、箭头函数
- 箭头函数只能简写函数表达式, 不能简写声明式函数
- 语法:
(函数的形参) => {函数体内要执行的代码}
let obj = {
fn1: function () { console.log(this) },
// 箭头函数内部是没有 this 的, 你在这个位置使用的 this,就相当于找到了上一层级最近的一个 this
fn2: () => { console.log(this) }
}
obj.fn1() // this === obj
obj.fn2() // this === window
1.写法
(1)简化写法:只有一个形参,小括号可省略
let fn = (a) => { console.log(a) }
let fn = a => { console.log(a) }
fn(100)
- 如果这一个形参有默认值, 那么必须添加小括号
let fn = (a = 200) => { console.log(a) }
fn(100)
(2)要执行的代码只有一行, 所以可以省略 {}
let fn = a => a
console.log(fn) // a => a
console.log(fn()) // undefined
console.log(fn(1123)) // 1123
2.箭头函数与普通函数 this 的区别
- (1)普通函数 this 指向与调用者
- (2)箭头函数 this 取决于上下文
四、解构赋值
快速的从对象或者数组中去除成员的一个语法方式
1.解构对象
// ES5 的方法想得到对象中的成员
const obj = {
name: 'Jack',
age: 18,
gender: '男'
}
let name = obj.name
let age = obj.age
let gender = obj.gender
// 解构赋值的方式从对象中获取成员
const obj = {
name: 'Jack',
age: 18,
gender: '男'
}
let { name, age, gender } = obj
/**
* 前面的 {} 表示我要从 obj 这个对象中获取成员了
* name age gender 都带是 obj 中有的成员
* obj 必须是一个对象
*/
2.解构数组
// ES5 的方式从数组中获取成员
const arr = ['Jack', 'Rose', 'Tom']
let a = arr[0]
let b = arr[1]
let c = arr[2]
// 解构赋值的方式从数组中获取成员
const arr = ['Jack', 'Rose', 'Tom']
let [a, b, c] = arr
/**
* 前面的 [] 表示要从 arr 这个数组中获取成员了
* a b c 分别对应这个数组中的索引 0 1 2 的值
* arr 必须是一个数组
*/
五、模板字符串
表示字符串的时候使用`` (反引号)
反引号与单(双)引号的区别
(1)反引号可以换行书写
let str = 'hello world' // 使用单引号或者双引号不能换行书写, 换行会报错
// 使用反引号不会报错, 这样写也不会报错
let str = `
hello
world
`
(2)反引号可以直接在字符串里拼接变量, 不过需要借助 ${}
// ES5 需要使用字符串拼接变量的时候
let num = 100
let str = 'hello' + num + 'world' + num
console.log(str)
let str2 = 'hellonumworldnum'
console.log(str2)
// 使用模板字符串
let num = 100
let str = `hello${num}world${num}`
console.log(str)
六、展开运算符
ES6 里面新增了一个运算符 ...,叫做扩展运算符
作用
1.把数组展开
let arr = [1, 2, 3, 4, 5]
console.log(...arr) // 1 2 3 4 5
2.合并数组的时候使用
let arr = [1, 2, 3, 4]
let arr1 = [...arr, 5, 6, 7]
console.log(arr1)
3.合并对象使用
let obj = {
name: 'Jack',
age: 18
}
let obj1 = {
...obj,
gender: '男'
}
console.log(obj1)
4.在函数传递参数时使用
let arr = [1, 2, 3]
function fn(a, b, c) {
console.log(a)
console.log(b)
console.log(c)
}
fn(...arr)
// 等价于 fn(1, 2, 3)
七、Map和Set 数据结构
共同点: 都不接受重复数据
1.Map 数据结构
- 创建一个 Map 数据结构
- 语法:
var m = new Map([ [key, value], [key, value] ])
// 创建了一个 空的 map 集合
const oMap = new Map()
1. 向 map 中添加数据set()
// 下边的三行代码也能执行, 但是不建议, 如果这样写和对象没有什么区别, 建议使用对象
oMap.set('key', 'value')
oMap.set('age', 18)
oMap.set('name', '张三')
// map 数据中, key 的值类型是没有限制的, 但是对象中, 只能是字符串
const arr = [1, 2, 3]
oMap.set(arr, [4, 5, 6])
console.log(oMap)
2. 获取 map 中的一些数据
console.log(oMap.get('age'))
console.log(oMap.get('name'))
console.log(oMap.get(arr))
3. has() 判断数据中是否拥有指定的 key
console.log(oMap.has('name')) // true
console.log(oMap.has('qwe')) // false
4. delete() 删除数据中的某一个
oMap.delete('key')
console.log(oMap)
5.clear() 清空数据集合
oMap.clear()
console.log(oMap)
6.forEach()
语法: 数据结构.forEach(function (value, key, origin) {})
oMap.forEach(function (value, key, origin) {
console.log(value, key)
})
2.Set 数据结构
Set 内部不允许数据重复,可以利用Set去重
- 创建 Set 数据结构
- 语法:
var s = new Set([数据1, 数据2, 数据2, 数据3, ... ])
const arr = new Set([100, 100, 2, 2, 3, 3, 4, 4, 5, 5])
1.向集合中 添加数据
oSet.add(10086)
oSet.add('QF001')
oSet.get() // oSet.get is not a function,set 数据集合没有提供 get 方法
2. 判断数据集合中是否拥有这个数据
console.log(oSet.has(5))
console.log(oSet.has(500))
3. delete 删除某一个
oSet.delete(1)
oSet.delete(2)
oSet.delete(3)
console.log(oSet)
4. clear() 清空数据集合
oSet.clear()
console.log(oSet)
5.forEach() 方法
语法: 数据结构.forEach(function (value, key, origin) {})
oSet.forEach(function (item, index, origin) {
/**
* item 和 origin 和 数组中 forEach 的含义是相同的
*
* 但是 index 不同, 因为 set 中 不允许使用 下标获取到内部的数据
* 所以 index 的值不是下标, 而是数组的每一个值
* 所以 在当前方法中, item 和 index 的值 永远相同
*/
console.log(item, index, origin)
})
利用 set 完成数组去重
const arr = [...new Set([100, 100, 2, 2, 3, 3, 4, 4, 5, 5])]
console.log(arr) // [100, 2, 3, 4, 5]
八、对象简写语法
-
- 当你的对象 key 和 value 一模一样的时候,并且 value 是一个变量,那么可以省略一个不写
var name = 'XXX'
var obj = {
name: name
}
var obj1 = {
name
}
- 当对象内 key 对应的值是一个函数,并且不是箭头函数的时候,可以省略 function 关键字和冒号 不写
var obj = {
fn: function () {
console.log(123)
},
fn1() {
console.log(456)
}
}
九、模块化开发
-
把完整功能,拆开成为一个一个的
独立功能(模块),一个 JS 文件就是一个独立模块,根据业务需求,来进行模块整合。 -
当模块化开发的时候,我们需要把多个逻辑书写在多个 JS 文件内,此时,每一个文件都是独立的文件,都是一个
独立的模块作用域(文件作用域),该文件内,只能使用自己文件内的变量,不能使用其他文件内的变量 -
注意:
- 浏览器使用 ES6 模块化语法的要求,
script 标签必须要有一个 type=module 的属性 - 页面必须要在服务器上打开(可以借助
live server)
- 浏览器使用 ES6 模块化语法的要求,
1. 导出: 在一个文件内向外暴露一些内容
- 语法1:
export default 你要导出的数据 - 一个文件只能导出一个 default 数据
const num = 100
const fn = () => {
console.log('我是 header.js')
}
const obj = {
num: num,
fn: fn
}
export default obj
- 语法2:
export 定义变量 = 值 - 一个文件可以导出多个新定义的变量
export const s = 100
export let a = 200
2. 导入: 导入该文件的同时, 拿到它向外暴露的内容
- 语法:
import 变量 from '文件'- 这个语法必须对应导出语法1
import myObj = from './index.js'
- 语法2:
import { 导出的内容 } from '文件'- 这个语法必须对应导出语法2
import {s, a} from './index.js'
十、拷贝对象
const obj = {
name: 'QF001',
age: 18,
info: {
username: 'ZSSS',
password: '123456'
}
}
// console.log('原对象: ', obj)
/**
* 创建一个 和 obj 对象一模一样但是地址不同的一个对象
* 标准: 修改 obj 对象的时候, 不会影响到新对象
*/
/**
* 因为 obj 对象是一个 引用数据类型, 所以在赋值的时候, 是将变量内部的地址, 给到了新变量种
* 所以此时 newObj 和 obj 是同一个引用地址
* 所以修改obj 会影响 newObj, 反过来也是相同
*/
// const newObj = obj
function fn(obj) {
/**
* 我们当前的 fn 函数, 会基于 一个形参(要求是一个对象)
* 创建一个和它一模一样, 但是地址不同的 对象
* 标准: 修改其中一个对象, 不会影响另外一个对象
*/
const newObj = {}
// 遍历 形参 obj, 拿到这对象的 所有属性和属性值, 然后添加到 newObj 中
for (let key in obj) {
// console.log(key, obj[key])
const value = obj[key]
if (typeof (value) === 'object') {
/**
* 如果当前分支执行, 说明 value 的值是一个对象
* 如果是一个对象我们就不能够直接进行赋值, 我们需要封装一个函数
* 这个函数会基于 一个形参(要求是一个对象); 创建一个和他一模一样, 但是地址不同的对象
*/
newObj[key] = fn(value)
} else {
// 如果当前分支执行, 说明 value 是基本数据类型, 那么直接赋值即可
newObj[key] = value
}
}
return newObj
}
const res = fn(obj) // fn 函数会基于全局变量 obj 帮我们创建一个和它一模一样, 但是不是一个地址的新对象
// console.log('拷贝后的对象: ', res)
res.name = 'res对象被我修改了'
obj.age = 10086
res.info.password = '我给 res 这个变量的密码修改了'
console.log('源对象: ', obj)
console.log('新对象: ', res)