函数?
函数概念:
function执行特殊任务的代码块
函数的作用:
函数可以实现代码复用,提高开发效率
函数命名规范 :
1.不能用关键字,有特殊含义的字符,JavaScript 内置的一些英语词汇.例如: let class等
2.只能用下划线、字母、数字、$组成,且数字不能开头
3.字母严格区分大小写,如 Age 和 age 是不同的变量
4.尽量使用小驼峰式命名法,例:getName
5.前缀为动词:
| 动词 | 含义 |
|---|---|
| can | 判断是否可执行某个动作 |
| is | 判断是否为某个值 |
| get | 判获取某个值 |
| set | 设置某个值 |
| load | 加载某些数据 |
函数返回值:
当调用某个函数,这个函数会返回一个结果出来,这就是有返回值的函数.
当函数需要返回数据出去时,用return关键字
语法: return 数据
例如 : function getSum(x,y) { return x + y}
return 注意点:1.在函数体中使用 return 关键字能将内部的执行结果交给函数外部使用
2.return 后面代码不会再被执行,会立即结束当前函数,所以 return 后面的数据不要换行写
3.return函数可以没有 return,这种情况函数默认返回值为 undefined
函数细节补充
1.两个相同的函数后面的会覆盖前面的函数
2.在Javascript中 实参的个数和形参的个数可以不一致
如果形参过多 会自动填上undefined (了解即可)
如果实参过多 那么多余的实参会被忽略 (函数内部有一个arguments,里面装着所有的实参)
3.函数一旦碰到return就不会在往下执行了 函数的结束用return
具名函数
具名函数的声明:
function 函数名(形参) { 函数体 }
具名函数调用:
函数名(实参)
匿名函数
匿名函数 : function () {函数体}
函数表达式:将匿名函数赋值给一个变量,并且通过变量名称进行调用 我们将这个称为函数表达式
let fn = function () {}
调用: fn ()
立即执行函数
无需调用,立即执行,其实本质已经调用了
作用: 避免全局变量之间的污染
两种书写方式 :
方式1 : ( function () {函数体} ) () ;
方式2 : ( function () {函数体} () ) ;
(1)当立即执行函数前面还有别的代码要执行,则要在前面加上分号
(2)多个立即执行函数之间也用分号隔开 不然会报错
回调函数
回调函数:被当做参数的函数,称为回调函数
回调函数通常出现在 : 定时器、事件处理函数、ajax
定时器 :
setInterval(function () {函数体},1000)
setTimeout(function () {函数体} ,1000 )
事件处理函数 :
元素.addEventListener ('事件类型',function () {})
ajax :
axios({
url:'请求路径',
method:'get',
data: { post请求参数},
params: { get请求参数}
}).then(res=>{
//成功回调
console.log(res)
})
function fn (n) {
}
// 被当做参数的函数,称为回调函数
// 此时的匿名函数为 回调函数,fn 为 高阶函数
fn (function () {})
间歇函数--- setInterval
<script>
// 创建(开启) 计时器
// 语法 : setInterval (函数,时间)
// 1.时间是以毫秒为单位 2.每隔多长的时间调用一次函数 3. 若函数为具名函数,则只写函数名 不写括号()
function fn () {
console.log('打印');
}
let timer1 = setInterval(fn, 1000)
let timer2 = setInterval(function() {
console.log('打印1');
},1000)
// 关闭定时器
// 定时器标识:创建定时器时 会自动返回定时器标识(数字)
// clearInterval(定时器标识)
// clearInterval(timer1)
clearInterval(timer2)
</script>
延时函数---setTimeout
延时函数: 延迟定时器,只出现一次
// 创建语法:setTimeout(函数, 延迟的时间);
const t2 = setTimeout(function (){
console.log(123)
//清除语法:clearTimeout(定时器标识)
clearTimeout(t2)
递归函数
递归函数: 函数内部调用自己
例如 : function fn () { fn() }
递归函数应用
应用1 :延时函数配合递归函数 得到的效果和间歇函数一样
// 延时函数配合递归函数 得到的效果和间歇函数一样
function show () {
let n = new Date ()
document.querySelector('div').innerHTML = n.toLocaleString()
// 2.延时函数做法:
setTimeout(show,1000)
}
show()
/* // 1.直接用间歇函数的做法:
show() //bug1 防止每次页面刷新时,有一秒的卡顿(出现div原本就有的"时间"两个字)
setInterval(show,1000) */
应用2 :深拷贝/浅拷贝
let obj = {
name:'张三',
age:20,
sex:'男',
hobby:['吃饭','睡觉','学习']
}
浅拷贝:拷贝地址,修改拷贝之后的数据对原数据有影响
// 浅拷贝:拷贝地址
let newObj = obj
newObj.name = '李四'
console.log(obj,newObj);
深拷贝: 拷贝数据,修改拷贝后的数据对原数据没有影响
深拷贝的两种方式:
方式1:json方式
(1)JSON.stringify(js对象):把js对象转换为接送字符串
(2)JSON.parse(json字符创): json字符串转为js对象
方式2:递归函数
// 深拷贝---json
/* let json = JSON.stringify(obj)
let newObj1 = JSON.parse(json) */
// 简写:
let newObj1 = JSON.parse(JSON.stringify(obj))
console.log(obj,newObj1);
// 深拷贝---递归函数
// 深拷贝函数
function copy(obj,newObj) {
// 遍历obj,把obj里面的数据 拷贝给newObj
for(let key in obj) {
// 判断obj里面的属性是不是数组
if (obj[key] instanceof Array) {
// 如果是,建立一个空数组
newObj[key] = []
// 递归函数--遍历数组
copy(obj[key],newObj[key] )
// 判断obj里面的方法
} else if (obj[key] instanceof Object) {
// 如果是方法,声明一个空对象
newObj[key] = {}
copy(obj[key],newObj[key] )
} else {
newObj[key] = obj[key]
}
}
}
// 创建一个空对象
let newObj2 = {}
// 调用copy函数
copy(obj,newObj2)
console.log(obj,newObj2);
应用3:递归函数的应用----遍历DOM树
//封装一个添加菜单的函数
function addElement(arr,father){
for(let i = 0;i<arr.length;i++){
let div = document.createElement('div')
div.innerHTML = `<p>${ arr[i].type || arr[i] }</p>`
father.appendChild(div)
if( arr[i].data ){
// 调用函数
addElement(arr[i].data , div )
}
}
}
//调用函数
addElement( arr, document.querySelector('.menu') )
箭头函数
箭头函数 : 相当于函数表达式的简写:
把function改成箭头 => (2)把形参() 写在箭头 左边
//匿名函数(函数表达式)
const fun = function() {
console.log(456);
}
fun()
// 1.箭头函数语法: (先声明再调用)
const fn = () => {
console.log(123);
}
fn()
// 2.箭头函数只有一个形参时,可以省略小括号
const fn1 = x => {
console.log(x);
}
fn1(1)
// 3.箭头函数只有一行代码的时候,可以省略花括号{}
const fn2 = x => console.log(x);
fn1(2)
// 4. 箭头函数只有一行代码的时候,可以省略return
const fn3 = x => x + x
/*
const fn3 = (x) => {
return x + x
}
*/
console.log(fn3(22));
// 5.箭头函数可以直接返回一个对象,返回对象要加()
const fn4 = (uname) => ({uname:uname})
console.log(fn4('剪刀手'));
// 6.箭头函数里面没有 arguments动态参数 只有剩余参数
// 箭头函数求和:
const fn5 = (...arr) => {
let sum = 0
for (let i = 0; i < arr.length; i++) {
sum += arr[i]
}
console.log(sum);
}
fn5 (1,2,3,4,5)
// 7.箭头函数不会 创建自己的this,他只会从自己的作用域的上一层找this
// 箭头函数this : 箭头函数没有this
// * 箭头函数中使用this, 本质是通过作用域链找上一级作用域的this
// DOM事件不推荐使用箭头函数
函数内的this指向
环境对象 this : 谁 ‘调用’ 我,this就指向谁说人话: this相当于中文中的 '我' , 谁说出来这个字,这个字就代表谁
this指向取决于函数的调用, 函数有三种调用方式(1)普通函数: 函数名() this->window
(2)构造函数: new 函数名() this->new创建的实例对象
(3)对象方法: 对象名.方法名() this->对象
一句话总结: this指向三选一, 先找new,后找点
补充:(1)定时器里的this默认指向window
(2) this只在函数中存在,箭头函数内部没有this
3.
箭头函数不会 创建自己的this,他只会从自己的作用域的上一层找this箭头函数this : 箭头函数没有this
- 箭头函数中使用this, 本质是通过作用域链找上一级作用域的this
4.拓展:
箭头函数没有this,对箭头函数影响(1)箭头函数不能作为构造函数
(2)箭头函数不能修改this
(3)事件处理函数一般不用箭头函数
修改this指向
默认情况下,函数内的this是固定的,无法被修改- 如果想要动态
修改函数内部的this指向,则需要使用上下文调用方法 - 上下文 : 函数作用域 上下文指向: 修改函数作用域内部this指向
- 上下文调用 : 修改函数内部this
-
函数名.call()
-
函数名.apply()
-
函数名.bind()
函数名.call()
//声明函数
function fn( a , b ) {
console.log(this)
console.log(a + b)
}
// 函数名.call(修改的this,参数1,参数2………)
fn.call( { name: '张三' } , 10 , 20 ) // call()
call()使用场景---检测数据类型
函数名.call() 应用 : 万能数据类型检测
typeof 检测数据: 检测数据类型,但是有两种数据类型无法检测
- typeof无法检测
数组 与 null, 得到的都是'object'
2.万能数据类型检测: Object.prototype.toString.call( 数据 ) :
万能数据类型检测原理 :
(1)Object.prototype.toString() 内部会返回this的数据类型, 得到固定格式字符串 '[object 数据类型]'
(2)使用Object原型中的toString()要想得到数据类型,只需要把this修改成你想要检测的对象
//值类型
let str = 'abc'
let num = 123
let bol = true
let und = undefined
let nul = null
//引用类型
let arr = [10,20,30]
let fn = function(){}
let obj = {name:'ikun'}
console.log( typeof str )//'string'
console.log( typeof num )//'number'
console.log( typeof bol )//'boolean'
console.log( typeof und )//'undefined' 未定义
console.log( typeof nul )//'object' 空值
console.log( typeof arr )//'object'
console.log( typeof fn )//'function'
console.log( typeof obj )//'object'
/* 万能数据类型检测原理
(1)Object.prototype.toString() 内部会返回this的数据类型, 得到固定格式字符串 '[object 数据类型]'
(2)使用Object原型中的toString()要想得到数据类型,只需要把this修改成你想要检测的对象
*/
console.log( Object.prototype.toString.call(str) )//'[object String]'
console.log( Object.prototype.toString.call(num) )//'[object Number]'
console.log( Object.prototype.toString.call(bol) )//'[object Boolean]'
console.log( Object.prototype.toString.call(und) )//'[object Undefined]'
console.log( Object.prototype.toString.call(nul) )//'[object Null]'
console.log( Object.prototype.toString.call(arr) )//'[object Array]'
console.log( Object.prototype.toString.call(fn) )//'[object Function]'
console.log( Object.prototype.toString.call(obj) )//'[object Object]'
函数名.apply()
函数名.apply(修改的this, 数组/伪数组 )
apply会自动遍历数组和伪数组,然后逐一传参
//声明函数
function fn(a, b) {
console.log(this)
console.log(a + b)
}
//(1) 函数名.call(修改的this,参数1,参数2………)
fn.call({ name: '张三' }, 10, 20)
//(2) 函数名.apply(修改的this, 数组/伪数组 )
// apply会自动遍历数组和伪数组,然后逐一传参
fn.apply({ name: "李四" }, [50, 60] )
apply()应用场景---伪数组转真数组
/*
apply()场景: 伪数组转真数组
1.伪数组 : 有 数组三要素(下标、元素、长度),不能使用数组的方法
* 伪数组本质是 对象
*/
let obj = {
0: 10,
1: 20,
2: 30,
length: 3
}
console.log(obj)
//需求: 有时候伪数组 想要使用 真数组的方法 , 就需要把伪数组转成真数组
//(1)把伪数组的元素取出来, push到真数组中
let arr = []
// arr.push(obj[0], obj[1], obj[2])
// console.log(arr)
//(2)手动写循环遍历添加
// for (let i = 0; i < obj.length; i++) {
// arr.push(obj[i])
// }
//(3) arr.push.apply( arr,伪数组 )
//这里使用apply不是为了修改this,而是借助传参特点:自动遍历伪数组/数组传参. 所以第一个参数应该写arr(保持this不变)
arr.push.apply(arr,obj)
console.log( arr )
//ES6 : 伪数组转真数组,固定静态方法 Array.from( 伪数组 )
let newArr = Array.from( obj )
console.log( newArr )
apply()使用场景--- 求数组最大值
/*
apply()场景 : 求数组最大值
*/
let arr = [20, 55, 60, 80, 100, 30, 40]
//(1)js基础 : 擂台思想
let max = arr[0]
for (let i = 1; i < arr.length; i++) {
if (arr[i] > max) {
max = arr[i]
}
}
console.log(max)
//(2)js高级 : Math.max()
// let max1 = Math.max(arr[0], arr[1], arr[2], arr[3], arr[4], arr[5], arr[6])
let max1 = Math.max.apply(Math, arr)
console.log(max1)
//ES6 : Math.max(...arr)
// ...功能类似于apply,也会自动的把数组给遍历
let max2 = Math.max(...arr)
console.log(max2)
函数名.bind()
函数名.bind(修改的this)
-
bind()不会立即执行函数,而是得到修改this的新函数。
-
bind()一般修改: 定时器函数 、 事件处理函数
//声明函数
function fn(a, b) {
console.log(this)
console.log(a + b)
}
//(3) 函数名.bind(修改的this)
// bind不会立即执行函数,而是得到一个修改this之后的新函数
let newFn = fn.bind({name:'王五'})
newFn(22,33)
bind()场景---修改定时器的this
//定时器中的this默认指向window,如果修改定时器的this,就需要使用bind
let fn = function (){
console.log(this)
}
let newFn = fn.bind({name:'111'})
setTimeout( newFn , 2000)
//上面步骤可以简写一行
setTimeout( function(){
console.log(this)
}.bind({name:'李四'}) , 2000 )
/*
变量 : 是内存空间. 只有存储功能,没有运算功能。
变量只有两种语法 : 存 , 取
字面量 : 是数据 。 只有运算功能,没有存储功能。
*/
call、apply、bind三者的区别
call 和 apply 和 bind 三者区别
相同点 : 都可以修改this指向
不同点 :
(1)传参方式不同 : call是单个传参, apply是数组/伪数组传参
(2)执行机制不同 : call和apply会立即执行函数, bind不会立即执行而是得到修改this的新函数