函数
-
函数定义方式:声明式,赋值式
-
自调用函数
//函数定义和调用写在一块 (function() { // 函数体 console.log('自调用函数') })() //封装代码 //封装私有变量,独立于全局变量 -
arguments
在函数内部自带的变量,表示所有实参的集合,是伪数组
function fn0{
console.log(arguments)
}
fn(10)
length属性实参个数
this关键字
-
this表示当前对象,在同场景下this表示不同的对象
-
函数内部的this指向谁,取决于函数的调用方式
-
全局定义的函数直接调用,this => window
function fn() { console.log(this) } fn()//此时this指向window
-
对象内部的方法调用,this =>调用者
var obj = { fn: function() { console.log(this) } } obj.fn() //此时this指向obj -
定时器的处理函数,this => window
setTimeout(function() { console.log(this) }, 0) //此时定时器处理函数里面的this指向window -
事件处理函数,this =>事件源
div.onclick = function () { console.log(this) } //当你点击div 的时候,this指向div -
自调用函数,this => window
(function () { console.log(this) })() (function ) { console.log('自调用函数') })() //此时this指向window
- 重点:函数内部的 this只和函数的调用方式有关系,和函数的定义方式没有关系
call 和 apply和 bind
- call
-
call方法是附加在函数调用后面使用,可以忽略函数本身的 this 指向
-
语法:函数名.call(要改变的this指向,要给函数传递的参数1,要给函数传递的参数2,...)
var obj = { name: 'Jack’} function fn(a,b){ console.log(this) console.log(a) console.log(b) } fn(1,2) fn.call(obj,1,2)- fn()的时候,函数内部的this指向window
- fn.call(obj,1,2)的时候,函数内部的 this 就指向了obj这个对象
- 使用call方法的时候,会立即执行函数,第一个参数是你要改变的函数内部的 this指向·第二个参数开始,依次是向函数传递参数
- apply
-
apply方法是附加在函数调用后面使用,可以忽略函数本身的this指向
-
语法:函数名.apply(要改变的 this 指向,[要给函数传递的参数1,要给函数传递的参数2, ...])
var obj = { name: "Jack”} function fn(a, b){ console.log(this) console.log(a) console.log(b) } fn(1,2) fn.call(obj,[1,2])- fn()的时候,函数内部的 this指向window
- fn.apply(obj,[1,2])的时候,函数内部的this 就指向了obj这个对象
- 使用apply方法的时候,会立即执行函数,第一个参数是你要改变的函数内部的this指向,第二个参数是一个数组,数组里面的每一项依次是向函数传递的参数
- bind
-
bind方法是附加在函数调用后面使用,可以忽略函数本身的this指向
-
和call / apply有一些不一样,就是不会立即执行函数,而是返回一个已经改变了this指向
-
函数语法:var newFn =函数名.bind(要改变的 this 指向);newFn(传递参数)
var obj = { name: "3ack’} function fn(a, b){ console.log(this) console.log(a) console.log(b) } fn(1,2) var newFn = fn.bind(obj)newFn(1,2)- bind调用的时候,不会执行fn这个函数,而是返回一个新的函数
- 这个新的函数就是一个改变了this指向以后的fn函数
- fn(1,2)的时候this指向window
- newFn(1,2)的时候执行的是一个和fn一摸一样的函数,只不过里面的this指向改成了obj
ES6新特性
1. let和const 关键字
-
let和const不允许重复声明变量
//使用var的时候重复声明变量是没问题的,只不过就是后面会把前面覆盖掉 var num = 100 var num = 200 //使用let重复声明变量的时候就会报错了 let num = 100 let num = 200 //这里就会报错了 //使用const重复声明变量的时候就会报错 const num = 100 const num = 200 //这里就会报错了 -
没有变量提升
//因为预解析((变量提升)的原因,在前面是有这个变量的,只不过没有赋值 console.log(num) // undefined var num = 100 //因为let不会进行预解析((变量提升),所以直接报错了 console.log(num) // undefined let num = 100 //因为const不会进行预解析(变量提升),所以直接报错了 console.log(num) // undefined const num = 100 -
let和const声明的变量会被所有代码块限制作用范围
//var声明的变量只有函数能限制其作用域,其她的不能限制 if (true){ var num = 100 } console.log(num) // 100 // let声明的变量,除了函数可以限制,所有的代码快都可以限制其作用域《if/while/forl . ..) if(true){ let num - 100 console.log(num) // 100 } console.log(num) //报错 // const声明的变量。除了函数可以限制,所有的代码块都可以限制其作用域(if/while/for/...) if (true){ const num = 100 console.log(num) // 100 } console.log(num)报错 -
let和const 的区别
let声明的变量的值可以改变,const声明的变量的值不可以改变
let num = 100
num = 200
console.log(num) //200
const num = 100
num = 200//这里就会报错了,因为 const 声明的变量值不可以改变(我们也叫做常量)
let声明的时候可以不赋值,const 声明的时候必须赋值
let num
num = 100
console.log(num) // 100
const num //这里就会报错了,因为const声明的时候必须赋值
2. 箭头函数
-
箭头函数是ES6里面一个简写函数的语法方式
-
重点:箭头函数只能简写函数表达式,不能简写声明式函数
function fn() {} //不能简写 const fun = function() {} //可以简写 const obj = { fn: function() {} //可以简写 } -
语法:(函数的行参) =>{函数体内要执行的代码}
const fn = function(a, b) { console.log(a) console.log(b) } //可以使用箭头函数写成 const fun = (a, b) => { console.log(a) console.log(b) } const obj = { fn: function(a, b) { console.log(a) console.log(b) } } //可以使用箭头函数写成 const obj2 = { fn: (a, b) => { console.log(a) console.log(b) } } -
箭头函数的特殊性
箭头函数教内部没有this,箭头函数的this是上下文的this
//在箭头函数定义的位置往上数,这一行是可以打印出this 的
//因为这里的this是window
//所以箭头函数内部的 this就是window
const obj = {
fn: function() {
console.log(this)
},
//这个位置是箭头函数的上一行,但是不能打印出this
fun: => {
//箭头函数内部的this 是书写箭头函数的上一行一个可以打印出 this的位置
console.log(this)
}
}
obj.fn()
obj.fun()
国数的行参只有一个的时候可以不写()其余情况必须写
const obj = {
fn: () => {
console.log('没有参数,必须写小括号')
},
fn2: a => {
console.log("一个行参,可以不写小括号')
},
fn3: (a, b) => {
console.log("两个或两个以上参数,必须写小括号")
}
}
函数体只有一行代码的时候,可以不写{},并且会自动return
const obj = {
fn: a => {
return a + 10
},
fun: a => a + 10
}
console.log(fn( 10)) // 20
console.log(fun(10)) // 20
3. 函数传递参数的时候的默认值
我们在定义函数的时候,有的时候需要—个默认值出现,就是当我不传递参数的时候,使用默认值,传递参数了就使用传递的参数
function fn(a){
a = a || 10
console.log(a)
}
fn()//不传递参数的时候,函数内部的a就是10
fn(20)//传递了参数20的时候,函数内部的a就是20
在ES6中我们可以直接把默认值写在函数的行参位置
function fn(a = 10){
console.log(a)
}
fn()//不传递参数的时候。函数内部的a就是10
fn(20)//传递了参数20的时候。函数内部的a就是20
这个默认值的方式箭头函数也可以使用
const fn = (a = 10) => {
console.log(a)
}
fn()//不传递参数的时候,函数内部的a就是10
fn(20)//传递了参数20的时候,函数内部的a就是28
注意:箭头函数如果你需要使用默认值的话,那么一个参数的时候也需要写О
4. 解构赋值
就是快速的从对象或者数组中取出成员的一个语法方式
- 解构对象
快速的从对象中获取成员
// ES5的方法向得到对象中的成员
const obj = {
name: "ack ",
age: 18,
gender:"男”
}
let name = obj.name
let age = obj.age
let gender = obj.gender
//解构赋值的方式从对象中获取成员
const obj = {
name: "ack ",
age: 18,
gender:"男"
}
//前面的表示我要从obj这个对象中获取成员了
// name age gender都得是obj中有的成员
// obj必须是一个对象
let { name,age,gender } = obj
- 解构数组
快速的从数组中获取成员
//ES5的方式从数组中获取成员
const arr = ['ack', 'Rose', "Tom']
let a = arr[0]
let b = arr[1]
let c = arr[2]
//使用解构赋值的方式从数组中获取成员
const arr = ['ack', 'Rose', ;Tom']
//前面的[]表示要从arr这个数组中获取成员了
// a b c 分别对应这数组中的索引0 1 2
// arr必须是一个数组
let [a,b,c] = arr
注意:是专门解构对象使用的]是专门解构数组使用的不能混用
-
交换变量
<script> function test1() { * 利用解构交换变量值 */ let a = 3 let b = 5;//这里必须加结束语句结束符 //利用解构交换变量值 [a, b] =[b,a] console.log('a ', a, 'b ', b); } test1() -
解析一个从函数中返回的数组
function func() { return [2,3]; } var a, b; [a, b] = func(); consoie. iog(a, b); undefined 2 3
5. 展开运算符
ES6里面号新添加了一个运算符...,叫做展开运算符
-
展开数组
let arr = [1,2,3,4,5] console.log( . ..arr) // 1 2 3 4 5 -
合并数组
let arr = [1,2,3,4] let arr2 = [...arr,5] console.log(arr2) -
合并对象
let obj = { name : "oack ', age: 18 } let obj2 = { ...obj, gender:'男' } console.log(obj2) -
函数传递参数
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)
6. 对象字面量简化写法
当属性与值的变量同名时,可以只写一个
const name = 'ane ';
const age = 20
//es6
const person = {
name,
age
}
//es5
var person = {
name: name,
age: age
};
7. Symbol简单数据类型
-
ES6引入Symbol的原因:ES5的对象属性名都是字符串,很容易造成属性名冲突。比如,使用了一个他人提供的对象,想为这个对象添加新的方法,新方法的名字就有可能与现有方法产生冲突。如果有—种机制,保证每个属性的名字都是独一无二的,这样就从根本上防止了属性名冲突。
-
用法:直接使用Symbol()创建新的symbol类型,并用一个可选的字符串作为其描述.
let sym1 = symbol(); let sym2 = Symbol( 'foo'); let sym3 = Symbol( 'foo'); //上面的代码创建了三个新的symbol类型。
注意:Symbol函数的参数只是表示对当前Symbol值的描述,因此相同参数的Symbol函数的返回值是不相等的。
-
类型检测
let symbol = Symbol("test symbol"); console.log(typeof symbol);// "symbol"
8. Map和Set复杂数据类型
Map和Set是ES6新增的两个数据类型都是属于内置构造函数,使用new的方式来实例化使用
Set
是一个数据集合,可以在new的时候直接向内部添加数据
const s = new Set
oconsole.log(s)
//实例化的时候直接添加数据要以数组的形式添加
const s = new Set([1, 2, 3, {}, function (){}, true, "hello"])
console.log(s)
看上去是一个类似数组的数据结构,但是不是,就是Set 数据结构
- 常用方法和属性
(1)size:用来获取该数据结构中有多少数据的
const s = new Set([1,2,3,{}, function () {}, true,'hello'])
console.log(s.size) // 7
(2)add:用来向该数据类型中追加数据
const s = new Set()
s.add(0)
s.add({})
s.add(function () {})
console.log(s.size) // 3
(3)delete:是删除该数据结构中的某一个数据
const s = new Set()
s.add(0)
s.add({})
s.add(function () {})
s.delete(0)
console.log(s.size) //2
(4)clear:清空数据结构中的所有数据
const s = new Set()
s.add(0)
s.add({})
s.add(function () {})
s.clear(0)
console.log(s.size) // e
(5)forEach:用来遍历Set 数据结构的方法
const s = new Set
(s.add(0)
s.add({})
s.add(function () {})
s.forEach(item => {
console.log(item) // 0 {} function () {}
})
(6)获取Set结构里面的数据需要借助一个...展开运算符
//把他里面的东西都放到一个数组里面去,然后再获取
const s = new Set([1,2,3,4,5,6])
const a = [...s]
console.log(a) // (6)[1,2,3,4,5,6]
console.log(a[0])// 1
console.log([...s][0])// 1
- Set数据类型特点
Set不允许存储重复的数据
const s = new Set( [1,2,3])
s.add(1) //此时size是3
s.add(2) //此时size是3
s.add(3) //此时size是3
s.add(4) //此时size是4
-
数组去重
let array = [1, 1, 1, 1, 2, 3, 4, 4, 5, 3]; let set = new Set(array); console.log(set) //=> Set {1,2,3,4,5}
Map 是一个数据集合,是一个很类似于对象的数据集合
const m = new Map()
console.log(m)
(1)值 = 值 的数据类型
const m = new Map([[{},{}],[function () {},function () {}],[true,1]])
console.log(m)
/*
Map(3) {{...} => {...}, f => f, true => 1}
size: (...)
_proto_: Map
[[Entries]]: Array(3)
0: {object => objecty
key : {}
value: {}
1: {function () {} => function () {}}
key : f {}
value: f {}
2: {true => 1}
key: true
value: 1
length: 3
*/
(2)常用方法和属性
-
size :用来获取该数据类型中数据的个数
const m = new Map([[0,0],[function () {},function () {}],[true,1]]) console.log(m.size) // 3 -
delete :用来删除该数据集合中的某—个数据
const m = new Map([[f,f],[function () {},function () {}],[true,1]]) m.delete(true) console.log(m.size)//2 -
set:用来向该数据集合中添加数据使用
const m = new Map() m.set({ name: 'jack' }, { age: 18 }) console.log(m.size) // 1 -
get :用来获取该数据集合中的某—个数据
const m = new Map() m.set({ name : "Jack"}, { age: 18 }) m.set(true,function () {}) console.log(m.get(true)) // function () {} -
clear :清除数据集合中的所有数据
const m = new Map() m.set({ name : "Jack" }, { age: 18 }) m.set(true,function () {}) m.clear() console.log(m.size) // 0 -
has :用来判断数据集合中是否存在菜一个数据
const m =new Map() m.set{ name: 'Jack'} , { age: 18 }) m.set(true,function () {}) console.log(m.has(true)) // true
9. for ... of
for-of可以遍历数组,字符串和Map,不能遍历对象遍历数组,访问数组元素
遍历字符串,访问字符串字符元素
let arr = [10,20,30]
let str = 'hello'
let map = new Map([["姓名","小王"],["年龄" , "23"]]) //for-of遍历数组,获取数组元素
for(let v of arr){
console.log(v);
}
//for-of 遍历字符串,获取字符元素
for(let v of str){
console.log(v);
}
//for-of遍历Map集合
for(let v of map){
console.log(v);
}
10. 模块化语法import / export
- 模块化的理解
什么是模块?
将一个复杂的程序依据一定的规则封装成几个块,块的内部数据是私有的,只是向外部暴露一些接口,与外部其它模块通信。
前端模块化开发中:一个js文件就是一个模块,在js文件模块中定义的数据是私有的,可以向外部暴露数据和方法,其它js文件模块可以引入暴露的方法数据进行使用。
-
Es6模块化
- Es6模块化语法
export命令用于规定模块的对外接口,import命令用于输入其他模块提供的功能。
/定义模块math.js/ var basicNum = 0; var add = function (a,b){ return a + b; }; export { basicNum,add };
/**引用模块**/ import { basicNum,add } from './math '; function test(ele) { ele.textcontent = add(99 + basicNum) ; }-
用到export default命令,为模块指定默认输出
// export-default.js export default function () { console.log( 'foo '); } // import-default.js import customName from './export-default '; customName(); // 'foo'
模块默认输出,其他模块加载该模块时,import命令可以为该匿名函数指定任意名字。
<script type = "module"> import {max} from './js/util.js' import min from './js/index.js' import './js/main.js' let m = min((20,34) document.querySelector('div').innerHTML = m </script>
js错误处理机制
JavaScript 解析或运行时,一旦发生错误,引擎就会抛出一个错误对象Error
1.常用错误对象
- SyntaxError 对象
SyntaxError对象是解析代码时发生的语法错误。
//变量名错误
var 1a;
// Uncaught SyntaxError: Invalid or Unexpected token
//缺少括号
console.log 'hello');
// Uncaught syntaxError: Unexpected string
- ReferenceError 对像
ReferenceError对象是引用一个不存在的变量时发生的错误。
//使用一个不存在的变量
unknownVariable
// Uncaught ReferenceError: unknownVariable is not defined
console.log(a)
//Uncaught ReferenceError: a is not defined
- TypeError对象
TypeError对象是变量或参数不是预期类型时发生的错误。Uncaught TypeError: Cannot read property
- RangeError 对象
RangeError对象是一个值超出有效范围时发生的错误。主要有几种情况,一是数组长度为负数,二是Number对象的方法参数超出范围,以及函数堆栈超过最大值。
//数组长度不得为负数
new Array(-1)
// Uncaught RangeError: Invalid array length
2.手动生成错误对象
var err1 =new Error('出错了! ');
var err2 = new RangeError('出错了,变量超出有效范围! ')
var err3 = new TypeError('出错了,变量类型无效! ');
err1.message //"出错了!"
err2.message //"出错了,变量超出有效范围!"
err3.message //"出错了,变量类型无效!"
3.throw语句
throw语句的作用是手动中断程序执行,抛出一个错误。
if (x < e){
throw new Error( 'x 必须为正数');
}
//Uncaught ReferenceError: x is not defined
对于JavaScript引擎来说,遇到throw语句,程序就中止了。引擎会接收到throw抛出的信息,
4.try...catch结构
一旦发生错误,程序就中止执行了。JavaScript提供了try...catch结构,允许对错误进行处理,选择是否往下执行。
try {
throw new Error('出错了!");
} catch (e) {
console.log(e.name + ": " + e.message);
console.log(e.stack);
}
5.finally代码块
try...catch结构允许在最后添加一个finally代码块,表示不管是否出现错误,都必需在最后运行的语句。
try {
throw new Error('出错了......);
console.log(此行不会执行");
} finally {
console.log('完成清理工作');
}