函数
创建函数的三种方式
1.function声明的普通函数(命名函数)
2.函数表达式(匿名函数)
3.new Function()
var fn =new Function(‘参数1’,‘参数2’,…,'函数体);
-
Function()里面的参数都是字符串格式
-
所有的函数都是Fnction的实例对象
-
函数也属于对象
<!DOCTYPE html>
<html lang="en"><head><meta charset="UTF-8"><meta http-equiv="X-UA-Compatible" content="IE=edge"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>创建函数的三种方式</title>
</head><body></body>
<script>// 自定义函数function fn() {}//函数表达式var fn2 = function() {}//new Function()var fn3 = new Function('a', 'b', 'console.log(a+b)');fn3(2, 5);console.dir(fn3);// 判断fn3是否是函数console.log(fn3 instanceof Object);
</script></html>
函数的不同调用方式
<!DOCTYPE html>
<html lang="en"><head><meta charset="UTF-8"><meta http-equiv="X-UA-Compatible" content="IE=edge"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>函数的不同调用方式</title>
</head><body><button>按钮</button>
</body>
<script>var bth = document.querySelector('button')//1.普通函数的调用方式function fn() {console.log('普通函数');}fn()//2.对象类函数的调用方式var obj = {say: function() {console.log('对象类函数');}}obj.say();//3.构造类函数的调用方式function Star(name, age) {this.name = name;this.age = age}let people1 = new Star('尧子陌', '24');console.log(people1);//4.事件函数的调用方式bth.onclick = function() {alert('事件函数')}//5.定时器函数的调用方式setTimeout(function() {console.log('定时器函数');}, 5000);//6.自执行函数(function() {console.log('自执行函数');})();
</script></html>
函数内部的this
函数内部的this指向,当我们调用的时候才确定
注意:自执行的函数中this指向的是window
<!DOCTYPE html>
<html lang="en"><head><meta charset="UTF-8"><title>函数的this指向</title></head><body><button>按钮</button></body><script>// 函数的调用方式// 1.普通函数的调用function fn() {console.log(this) //windows}fn()// 2.对象类的函数调用var obj = {say: function() {console.log(this) //指向o这个对象}}obj.say()// 3.构造函数的调用function Star(name, age) {console.log(this) //构造函数中的this指向的是它的实例化 starthis.name = name;this.age = age;}var star1 = new Star('尧子陌', '18');//4.事件函数的调用var bth = document.querySelector('button');bth.addEventListener('click', function() {console.log(this) //事件函数中的this 指向的是它的调用者bth按钮})// 5.定时器函数setInterval(function() {console.log(this) //定时器中的this 指向的是windows}, 5000)</script>
</html><!DOCTYPE html>
<html lang="en"><head><meta charset="UTF-8"><meta http-equiv="X-UA-Compatible" content="IE=edge"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>函数中的this指向</title>
</head><body><button>按钮</button>
</body>
<script>var bth = document.querySelector('button')//1.普通函数的thisfunction fn() {console.log(this); //windows}fn()//2.对象类函数的thisvar obj = {say: function() {console.log(this + 'obj'); //obj}}obj.say();//3.构造类函数的thisfunction Star(name, age) {console.log(this); //people1this.name = name;this.age = age}let people1 = new Star('尧子陌', '24');//4.事件函数的thisbth.onclick = function() {console.log(this); //bth按钮}//5.定时器函数的thissetTimeout(function() {console.log(this); //windows}, 5000);//6.自执行函数(function() {console.log(this); //windows})();
</script></html>
call() apply() bind()
call()
call() 方法调用一个对象。简单理解为调用函数的方式,但是它可以改变函数的 this 指向。
fun.call(thisArg, arg1, arg2, …)
- thisArg:在 fun 函数运行时指定的 this 值
- arg1,arg2:传递的其他参数
- 返回值就是函数的返回值,因为它就是调用函数
- 因此当我们想改变 this 指向,同时想调用这个函数的时候,可以使用 call,比如继承
<!DOCTYPE html>
<html lang="en"><head><meta charset="UTF-8"><meta http-equiv="X-UA-Compatible" content="IE=edge"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>call()</title>
</head><body></body>
<script>// 1.call()改变this指向//声明一个对象var obj = {name: 'YaoZiMo'}function fn(a, b) {console.log(this); //objconsole.log(a + b);}//调用函数 并改变函数的this指向且传递参数fn.call(obj, 2, 6)// call()主要是为了实现继承function Father(name, age, sex) {this.name = name;this.age = age;this.sex = sex}function Son(name, age, sex) {// 继承父类的属性与方法Father.call(this, name, age, sex)}var son = new Son('尧子陌', '18', '男');console.log(son);
</script></html>
apply()
apply() 方法调用一个函数。简单理解为调用函数的方式,但是它可以改变函数的 this 指向。
fun.apply(thisArg, [argsArray])
- bthisArg:在fun函数运行时指定的 this 值
- argsArray:传递的值,必须包含在数组里面
- 返回值就是函数的返回值,因为它就是调用函数
- 因此 apply 主要跟数组有关系,比如使用 Math.max() 求数组的最大值
<!DOCTYPE html>
<html lang="en"><head><meta charset="UTF-8"><title>改变函数内部的this指向之apply()</title>
</head><body></body>
<script>var o = {name: 'andy'}function fn(arr) {console.log(this); //此时的this指向的是0;console.log(arr)}fn.apply(o, ['pink']); //注意:第二个参数必须为数组// 可以用数组方法实现求数组的最大值var arr2 = [1, 66, 22, 55, 108];var max = Math.max.apply(Math, arr2);var min = Math.min.apply(Math, arr2);console.log(max, min)
</script></html>
bind()
bind() 方法不会调用函数。但是能改变函数内部this 指向
fun.bind(thisArg, arg1, arg2, …)
- thisArg:在 fun 函数运行时指定的 this 值
- arg1,arg2:传递的其他参数
- 返回由指定的 this 值和初始化参数改造的原函数拷贝
- 因此当我们只是想改变 this 指向,并且不想调用这个函数的时候,可以使用 bind()
<!DOCTYPE html><html lang="en"><head><meta charset="UTF-8"><title>改变函数的内部的this指向之bind()方法</title><body><button>按钮</button><button>按钮</button><button>按钮</button></body><script>// bind()方法是绑定 捆绑的意思var o = {name: "andy"}function fn(a, b) {console.log(this)console.log(a + b)}// 注意:bind()方法会改变函数内部的this,但不会调用原来的函数var f = fn.bind(o, 1, 2);f()//案例:当我们点击按钮之后,就禁用这个按钮,三秒之后再次开启这个按钮var bths = document.querySelectorAll('button');for (var i = 0; i < bths.length; i++) {bths[i].onclick = function() {this.disabled = true;setTimeout(function() {this.disabled = false// 这里的this指的是bth这个按钮}.bind(this), 2000)}}</script>
</html><!DOCTYPE html>
<html lang="en"><head><meta charset="UTF-8"><meta http-equiv="X-UA-Compatible" content="IE=edge"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>bind()</title>
</head><body><button>按钮</button><button>按钮</button><button>按钮</button><button>按钮</button><button>按钮</button><button>按钮</button>
</body>
<script>// bind()改变this指向 但不会调用函数var obj = {name: 'YaoZiMo'}function fn(a, b) {console.log(this); //objconsole.log(a + b);}var f = fn.bind(obj, 10, 15)f()案例:当我们点击按钮之后,就禁用这个按钮,三秒之后再次开启这个按钮;var bth = document.querySelectorAll('button')for (var i = 0; i < bth.length; i++) {bth[i].onclick = function() {this.disabled = true;setTimeout(function() {// 三秒之后开启按钮this.disabled = false}.bind(this), 3000)}}
</script></html>
call() apply() bind()之间的区别
区别点:
1.call 和 apply 会调用函数, 并且改变函数内部this指向.
2.call 和 apply 传递的参数不一样, call 传递参数 aru1, aru2…形式 apply 必须数组形式[arg]
3.bind 不会调用函数, 可以改变函数内部this指向.
主要应用场景:
1.call 经常做继承.
2.apply 经常跟数组有关系. 比如借助于数学对象实现数组最大值最小值
3.bind 不调用函数,但是还想改变this指向. 比如改变定时器内部的this指向.
严格模式
严格模式的出现,是为了限制非标准严格模式下不标准的语法,并且为ES6做好铺垫
严格模式对正常的 JavaScript 语义做了一些更改:
1.消除了 Javascript 语法的一些不合理、不严谨之处,减少了一些怪异行为。
2.消除代码运行的一些不安全之处,保证代码运行的安全。
3.提高编译器效率,增加运行速度。
4.禁用了在 ECMAScript 的未来版本中可能会定义的一些语法,为未来新版本的 Javascript 做好铺垫
如一些保留字如:class, enum, export, extends, import, super 不能做变量名
开启严格模式
‘use strict’ 在script的脚本内放入或在函数内部放入,便等同于开启严格模式
<!DOCTYPE html>
<html lang="en"><head><meta charset="UTF-8"><meta http-equiv="X-UA-Compatible" content="IE=edge"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>严格模式</title>
</head><body></body>
<script>// 开启全局严格模式'use strict';function fn() {// 开启局部严格模式'use strict'}fn()
</script></html>
严格模式下的变化
1.变量必须先声明才能使用
2.禁止删除已声明的变量
3.普通函数中的this指向undefined
4.构造函数不加new调用,this指的是undefined
5.定时器中的this指向window
6.函数的参数 禁止重名
<!DOCTYPE html>
<html lang="en"><head><meta charset="UTF-8"><meta http-equiv="X-UA-Compatible" content="IE=edge"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>严格模式下的变化</title>
</head><body></body>
<script>'use strict'// 1.变量必须先声明再使用let num = 10;console.log(10);//2.禁止删除以声明的变量// delete num;//3.普通函数中的this指向undefinedfunction fn() {console.log(this); //undefined}fn()//4.构造函数不加new调用,this指的是undefinedfunction Fun() {console.log(this);}Fun()//5.定时器中的this指向windowsetTimeout(function() {console.log(this); //windows}, 3000)//6.函数的参数禁止重名function fun(a, b) {console.log(a + b);}fun(5, 8)
</script></html>
高阶函数
高阶函数是对其它函数进行操作的函数,它接受函数作为参数或者将函数作为返回值输出
<!DOCTYPE html>
<html lang="en"><head><meta charset="UTF-8"><meta http-equiv="X-UA-Compatible" content="IE=edge"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>高阶函数</title>
</head><body></body>
<script>function fn(a, b, callback) {console.log(a + b);callback && callback()}fn(10, 15, function() {console.log('高阶函数的第一种形式');})function fun() {return function() {return '高阶函数的第二种形式'}}console.log(fun());
</script></html>
回调函数
函数也是一种数据类型,可作为参数传递给另外一个函数作为参数使用 JQuery中最为常见。
<!DOCTYPE html>
<html lang="en"><head><meta charset="UTF-8"><meta http-equiv="X-UA-Compatible" content="IE=edge"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>回调函数</title><style>.box {position: absolute;top: 0;left: 0;width: 400px;height: 400px;background-color: red;}</style>
</head><body><div class="box"></div>
</body>
<script src="./jquery-3.6.0.js"></script>
<script>$('.box').animate({top: 500,left: 500,}, function() {$(this).css('backgroundColor', 'purple')})
</script></html>
变量作用域
全局变量 局部变量
-
函数内部可以使用全局变量。
-
函数外部不可以使用局部变量。
-
当函数执行完毕,本作用域内的局部变量会销毁。
闭包
指有权访问另外一个函数作用域里面的变量的函数
作用:延伸变量的作用范围
<!DOCTYPE html>
<html lang="en"><head><meta charset="UTF-8"><meta http-equiv="X-UA-Compatible" content="IE=edge"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>闭包</title>
</head><body></body>
<script>function fun() {var num = 10;function fn() {console.log(num);};fn()}fun()
</script></html>
chrome中调试闭包
1.打开浏览器,按 F12 键启动 chrome 调试工具。
2.设置断点。
3.找到 Scope 选项(Scope 作用域的意思)。
4.当我们重新刷新页面,会进入断点调试,Scope 里面会有两个参数(global 全局作用域、local 局部作用域)。
5.Closure 参数代表闭包函数
闭包函数
<!DOCTYPE html>
<html lang="en"><head><meta charset="UTF-8"><meta http-equiv="X-UA-Compatible" content="IE=edge"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>闭包</title>
</head><body></body>
<script>function fun() {var num = 10;return function fn() {console.log(num);};fn()}var fum = fun();fum()
</script></html>
闭包之输出li所在的索引号
核心思路:利用立刻执行函数便可解决此问题
<!DOCTYPE html>
<html lang="en"><head><meta charset="UTF-8"><meta http-equiv="X-UA-Compatible" content="IE=edge"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>闭包之输出li所在的索引号</title>
</head><body><ul class="nav"><li>One</li><li>Two</li><li>Three</li><li>Four</li><li>Five</li></ul>
</body>
<script>//获取所有的livar lis = document.querySelectorAll('li');/* 1.利用Js获取li所在的索引号for (var i = 0; i < lis.length; i++) {// 循环为每个li设置索引号lis[i].index = i;lis[i].onclick = function() {console.log(this.index);}}*///2.利用闭包获取li所在的索引号for (var i = 0; i < lis.length; i++) {// 利用循环创建多个自执行函数 自执行函数可以保存i(function(i) {lis[i].onclick = function() {console.log(i);}}(i))}
</script></html>
闭包之定时器应用
要求:三秒之后自动显示li里面的内容
核心思路:利用for循环的每次循环生成的闭包函数即可
<!DOCTYPE html>
<html lang="en"><head><meta charset="UTF-8"><meta http-equiv="X-UA-Compatible" content="IE=edge"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>闭包之输出li所在的索引号</title>
</head><body><ul class="nav"><li>One</li><li>Two</li><li>Three</li><li>Four</li><li>Five</li></ul>
</body>
<script>//获取所有的livar lis = document.querySelectorAll('li');//利用闭包获取li所在的索引号for (var i = 0; i < lis.length; i++) {// 利用循环创建多个自执行函数 自执行函数可以保存i(function(i) {setTimeout(function() {console.log(lis[i].innerHTML);}, 3000)}(i))}
</script></html>
闭包之打车价格
打车起步价13(3公里内), 之后每多一公里增加 5块钱. 用户输入公里数就可以计算打车价格
如果有拥堵情况,总价格多收取10块钱拥堵费
<!DOCTYPE html>
<html lang="en"><head><meta charset="UTF-8"><meta http-equiv="X-UA-Compatible" content="IE=edge"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>闭包之打车价格</title>
</head><body></body>
<script>let car = (function() {//初始价格var star = 13;//总价格var total = 0;return {price: function(n) {if (n <= 3) {total = star} else {total = star + (n - 3) * 5}//返回价格return total},stop: function(flag) {return flag ? total + 10 : total}}})()console.log(car.price(8));console.log(car.stop(true));console.log(car.price(8));console.log(car.stop(false));
</script></html>
闭包之测试题
<!DOCTYPE html>
<html lang="en"><head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><meta http-equiv="X-UA-Compatible" content="ie=edge"><title>Document</title>
</head><body><script>// 思考题 1:var name = "The Window";var object = {name: "My ObjectMy Object",getNameFunc: function() {return function() {return this.name;};}};console.log(object.getNameFunc()()) // The Window// 思考题 2:var name = "The Window";var object = {name: "My Object",getNameFunc: function() {var that = this;return function() {return that.name;};}};console.log(object.getNameFunc()()) // My Object</script>
</body></html>
、
递归函数
函数在内部可以调用其本身,这个函数就是递归函数
简单理解:函数内部自己调用自己
递归函数的作用和循环效果一样,很容易发生‘栈溢出’,所以必须要加退出条件return
递归函数之习题一
<!DOCTYPE html>
<html lang="en"><head><meta charset="UTF-8"><meta http-equiv="X-UA-Compatible" content="IE=edge"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>递归函数</title>
</head><body></body>
<script>var num = 1;function fn() {console.log('打印' + num + '次');if (num === 5) {return}num++fn()}fn()
</script></html>
递归函数之求1~n的阶乘
<!DOCTYPE html>
<html lang="en"><head><meta charset="UTF-8"><meta http-equiv="X-UA-Compatible" content="IE=edge"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>递归函数之求1~n的阶乘</title>
</head><body></body>
<script>function fn(n) {if (n === 1) {return 1}return n * fn(n - 1)}console.log(fn(4));//return 4 *fn(3)//return 4 *(3*fn(2))// return 4* (3*2*1)// return 24
</script></html>
递归函数之求斐波那契数列(兔子序列)
求斐波那契数列(兔子序列) 1、1、2、3、5、8、13、21…
<!DOCTYPE html>
<html lang="en"><head><meta charset="UTF-8"><meta http-equiv="X-UA-Compatible" content="IE=edge"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>递归函数之求斐波那契数列(兔子序列) </title>
</head><body></body>
<script>function fn(n) {if (n === 1 || n === 2) {return 1}return fn(n - 1) + fn(n - 2)}console.log(fn(6));</script></html>
利用递归来实现遍历数据
<!DOCTYPE html>
<html lang="en"><head><meta charset="UTF-8"><meta http-equiv="X-UA-Compatible" content="IE=edge"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>利用递归来实现遍历数据</title>
</head><body></body>
<script>var data = [{id: 1,name: '家电',goods: [{id: 11,gname: '冰箱',goods: [{id: 111,gname: '海尔'}, {id: 112,gname: '美的'}, ]}, {id: 12,gname: '洗衣机'}]}, {id: 2,name: '服饰'}];//根据id遍历出对象function getID(json, id) {var obj = {};json.forEach(function(item) {// 得到外层数据if (item.id == id) {obj = item} else if (item.goods && item.goods.length > 0) {//得到里层数据obj = getID(item.goods, 12)}})return obj}console.log(getID(data, 1));console.log(getID(data, 12));
</script></html>
Object.assign()(浅拷贝)
Object.assign() 方法用于将所有可枚举属性的值从一个或多个源对象分配到目标对象。它将返回目标对象。
<!DOCTYPE html>
<html lang="en"><head><meta charset="UTF-8"><meta http-equiv="X-UA-Compatible" content="IE=edge"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>Object.assign()(浅拷贝)</title>
</head><body></body>
<script>var obj = {name: '尧子陌',sex: '男',msg: {say: 'Hello Word'}}var o = {}// 进行浅拷贝Object.assign(o, obj)console.log(o);
</script></html>
深拷贝
<!DOCTYPE html>
<html lang="en"><head><meta charset="UTF-8"><meta http-equiv="X-UA-Compatible" content="IE=edge"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>深拷贝</title>
</head><body></body><script>var obj = {name: '尧子陌',sex: '男',msg: {say: 'Hello Word'},color: ['pink', 'red']}var o = {};function deepCopy(newobj, oldobj) {for (var k in oldobj) {var item = oldobj[k];// 如果属性值为数组if (item instanceof Array) {newobj[k] = [];deepCopy(newobj[k], item)} else if (item instanceof Object) {// 如果属性值为对象newobj[k] = {},deepCopy(newobj[k], item)} else {newobj[k] = item}}}deepCopy(o, obj);console.log(o);
</script></html>