目录
1.函数的定义方式
2.函数的调用方式以及this指向
3.改变函数中的this指向
4.严格模式
5.高阶函数
6.闭包
7.递归
8.拷贝
1.函数的定义方式
(1)自定义函数(命名函数)
function name(params) {}
(2)函数表达式(匿名函数)
var fun=function name(params) { }
(3)利用new Function('参数1',"参数2”,函数体)(了解,因为其效率不高)
这种方式其实就是构造函数,Function里面参数必须都是字符串格式
var f=new Function ('a','b','console.log(a+b)')
f(1,2);//3
所有的函数都是Function的实例对象,函数也属于对象
2.函数的调用方式以及this指向(1)普通函数 this指向window
(2)对象方法 this指向的对象是对象
(3)构造函数 this指向实例对象 原型对象里面的this指向的也是实例对象
(4)绑定事件函数 this指向函数的调用者 btn这个按钮对象
(5)定时器函数 this指向window
(6)立即执行函数 this指向window
// 函数的调用方式
// (1)普通函数 this指向window
function fn() {
console.log('普通函数的this'+this);
}
fn()
fn.call()
// (2)对象方法 this指向的对象是o
var o={
sayHi:function(){
console.log('对象方法的this'+this);
}
}
o.sayHi();
// (3)构造函数 this指向实例对象 原型对象里面的this指向的也是实例对象
function Star(){}
Star.prototype.sing=function(){}
var ldh=new Star();
// (4)绑定事件函数 this指向函数的调用者 btn这个按钮对象
btn.onclick=function(){}//点击按钮就可以调用这个函数
//(5)定时器函数 this指向window
setInterval(function(){},1000)
// (6)立即执行函数 this指向window
(function () {
console.log('立即执行函数的this'+this);
})()
// 立即执行函数是自动调用
3.改变函数中的this指向
(1)call()方法
这个方法可以调用对象,简单的理解为调用函数的方式,但是他可以改变函数的this指向
语法:fun.call(thisArg,arg1,arg2,……)
作用一:调用函数,改变函数内部的this指向
作用二:call的主要作用可以实现继承
// 1.call()
var o={
name:'hello'
}
function fn(a,b) {
console.log(this);
console.log(a+b);
}
fn.call(o,1,2)
// 作用一:调用函数,改变函数内部的this指向
// 作用二:call的主要作用可以实现继承
function Father(name,age,sex){
this.name=name
this.age=age
this.sex=sex
}
function Son(name,age,sex) {
// call后面可以指定参数,第一个是指定this所指向的函数,后面是所传入的参数
/*在调用父构造函数的同时,把父构造函数中的this指向子构造函数中的this,这样子构造函数就可
以使用父构造函数中的this*/
Father.call(this,name,age,sex)
}
var son=new Son('张三',18,'男')
console.log(son);
(2).apply方法
这个方法可以调用一个函数。可以理解为调用函数的方式,它可以改变函数的this指向
语法:fun.apply(thisAry,[argsArray])
thisAry:在fun函数运行是指定的this值
argsArray:传递的值,必需包含在数组里面
返回值是函数的返回值,因为他就是调用函数
作用:
1.调用函数,第二个可以改变函数内部的this指向,它的第二个参数必须是数组(伪数组)
2.apply的主要应用 比如说可以利用apply借助于数学内置对象求最大值
var o={
name:'hello'
};
function fn(arr){
console.log(this);//name
console.log(arr);//'pink'
}
// 此处的参数2必须为数组
fn.apply(o,['pink'])
// 3.apply的主要应用 比如说可以利用apply借助于数学内置对象求最大值
// Math.max()
var arr=[12,33,55,3,6,88]
/*
下面这个代码在执行的时候调用的是Math.max方法,我们不需要改变this指向,所以第一个参数为null,
参数二是数组,数组会把数据传递给前面的方法
但是在js语法规范中,参数一写null可能会不合适,所以说就直接写自己
*/
// var max=Math.max.apply(null,arr)
var max=Math.max.apply(Math,arr)
console.log(max);//88
(3)bind方法
如果有的函数我们不需要立即调用,但是又想改变这个函数内部的this指向,此时使用bind,因为call和apply都是会立即调用的。与此同时使用bind可以减少声明的次数,节约内存空间。
该方法不会调用函数,但是能改变函数内部的this指向
语法:fun.bind(thisAry,arg1,arg2)
参数:
thisAry:在fun函数运行是指定的this值
arg1,arg2传递的其他参数
返回值由指定的个this值和初始化参数改造的原函数拷贝
var o={
name:'hello'
}
function fn(a,b) {
console.log(this);
console.log(a+b);
}
// 此时bind只是绑定,并没有调用,所以说f需要接收
// bind改变了this的指向,指向o
var f=fn.bind(o,1,2);
// 此时的返回值是原函数改变this之后所产生的新函数,也就是f
f()
案例
但是如果有多个按钮的话,bind作用会更明显
调用函数 | 参数 | 应用场景 | |
call() | 立即调用 | 任意 | 常用作继承 |
apply() | 立即调用 | 数组形式 | 与数组有关,比如借助数学对象实现数组最大值最小值 |
bind() | 不会立即调用 | 任意 | 不调用函数,但是想改变this指向,比如改变定时器内部的this指向 |
js除了提供正常模式之外,还提供了严格模式,ES5的严格模式是采用具有限制性js变体的一种方式,即在严格的条件下运行js代码。严格模式消除了代码运行的一些不安全之处,保证代码运行安全,提高编译器效率,增加运行速度。严格模式在IE10版本以上才会被支持,就浏览器中容易被忽略
分类:严格模式可以运用到整个脚本或者是个别函数中,所以在使用的时候,可以将严格模式分为为脚本开启严格模式和为函数开启严格模式
(1)为脚本开启严格模式:需要在所有语句之前放一个特定语句 “use strict”(或者‘use strict’)
有的script标签基本是严格模式,有的是正常模式,这样不利于合并文件,所以可以将整个脚本文件放在一个立即执行的匿名函数中,这样独立创建一个作用域而不影响其他的script脚本
(2)为函数开启严格模式
开启严格模式之后
this的变化
(1)变量名必需先声明再使用
(2)不能随意删除已经声明好的变量
(3)严格模式下全局作用域函数中的this是undefined
(4)严格模式下如果构造函数不加new调用,this会报错
(5)定时器里面的this指向还是window
(6)事件.对象还是指向调用者
函数的变化
(1)严格模式下函数的参数不能重名
(2)函数声明必须在顶层
更多严格模式参考JavaScript 严格模式(use strict) | 菜鸟教程
5.高阶函数高阶函数是对其它函数进行 *** 作的函数,它接收函数作为参数或者将函数作为返回值输出
// 高阶函数-函数可以作为参数传递
function fn(a,b,callback){
console.log(a+b);
callback&&callback()
}
// 整个过程先执行fn传入参数的函数,再执行回调函数
fn(1,2,function(){
console.log('我是最后调用的函数');
})
6.闭包
(1)闭包:指有权访问另一个函数作用域中变量的函数。简单的理解就是一个作用域可以访问另外一个函数内部的局部变量
(2)闭包的产生:被访问的变量所在的函数就是闭包函数
// 闭包的产生:fun这个函数作用域访问了另外一个函数fn里面的局部变量num
// 被访问的变量所在的函数就是闭包函数,此时fn为闭包函数
function fn(){
var num=10;
function fun() {
console.log(num);
}
fun()
}
fn()
(3)闭包的作用:延伸了变量的作用范围
下面这个fn执行之后返回函数fun,将fun赋值给f,f先执行var 然后进入到fun函数中,最后输出。只有当所有的函数执行完毕之后num才会被销毁。return在这当中起了一个非常重要的作用,下面这个就相当于子函数可以使用父函数的的变量。return也是闭包中实现的一个原理
// fn外面的作用域可以访问fn内部的局部变量
function fn(){
var num=10;
/* function fun() {
console.log(num);
}
return fun; */
return function fun() {
console.log(num);
}
}
var f=fn();
f()//10
// 上面这一步就相当于下列写法
/* var f= function fun() {
console.log(num);
} */
(4)闭包应用
// 闭包应用-点击li输出当前li的索引号
// 1.利用动态添加属性的方式
var lis=document.querySelector('.nav').querySelectorAll('li')
for (let i = 0; i < lis.length; i++) {
lis[i].index=i
lis[i].onclick=function(){
// console.log(i);
console.log(this.index);
};
}
// 2.利用闭包的方式得到当前小li的索引号
for(var i=0;i
// 闭包应用-计算打车价格
// 打车起步价13(3公里内),之后每多一公里增加5块钱,用户输入公里数就可以计算打车价格
// 如果有拥堵情况,总价格多收取10块钱拥堵费
// function fn(){}
// fn()
var car=(function(){
var start=13//起步价
var total=0//总价
return{
//正常的总价
price:function(n){
if(n<=3){
total=start
}else{
total=start+(n-3)*5
}
return total;
},
//拥堵之后的费用
yd:function(flag){
return flag?total+10:total
}
}
})()
console.log(car.price(5));//23
console.log(car.yd(true));//33
console.log(car.price(1));//13
console.log(car.yd(false));//23
7.递归
如果一个函数在内部可以去调用其本身,那么这个函数就是递归函数。
简单理解:函数内部自己调用自己,这个函数就是递归函数
递归函数的作用和循环效果一样,由于递归很容易发生‘栈溢出’错误,所以必须要加退出条件return
// 递归函数:函数内部自己调用自己,这个函数就是递归函数
var num=1
function fn(){
console.log('我要打印六句话');
if(num==6){
return;//递归里面必须加退出条件
}
num++
fn()
}
fn()
利用递归求阶乘1*2*3*4*....n
// 利用递归求1~n的阶乘 1*2*3*4....n
function fn(n) {
if(n==1){
return 1
}
return n*fn(n-1)
}
console.log(fn(3)); //6
// 详细思路 加入用户输入的是3,每次调用fn保留前面的n
// return 3*fn(2)
// return 3*(2*fn(1))
// return 3*(2*1)
// return 3*(2)
// return 6
利用递归函数求斐波那契数列(兔子数列)1 1 2 3 5 8....
// 利用递归函数求斐波那契数列(兔子数列)1 1 2 3 5 8....
// 用户输入一个数字n就可以求i出这个数字对应的兔子序列值
// 只需要知道用户输入的第n项的前面两项(n-1)(n-2)就可以计算出n对应的序列值
function fn(n) {
if (n===1||n===2) {
return 1;
}
return fn(n-1)+fn(n-2)
}
console.log(fn(6));//8
利用递归遍历数据
遍历外层数据使用第一个if,遍历第二层数据符合条件的来到第二层,然后再一次调用getID,返回之后再一次执行第一个if
var data=[{
id:1,
name:'家电',
goods:[{
id:11,
gname:'冰箱',
goods:[{
id:111,
gname:'hair'
},
{
id:112,
gname:'美的'
}
]
},{
id:12,
gname:'洗衣机'
}]
},{
id:2,
name:'服饰'
}]
// 我们想要输入id号,就可以返回的相应的数据对象
//1.利用forEach去遍历里面的每一个对象
function getID(json,id){
var o={}
json.forEach(function(item){
// console.log(item);//2个数组元素
if(item.id==id){
// console.log(item)
o=item
// 2.想要得到最里面的数据 11 12可以利用递归函数
// 里面应该有goods数组并且数组不是空的
}else if(item.goods&&item.goods.length>0){
o= getID(item.goods,id)
}
});
return o;
}
console.log(getID(data,1))
console.log(getID(data,2))
console.log(getID(data,11))
console.log(getID(data,22))
console.log(getID(data,111))
8.拷贝
(1)浅拷贝
浅拷贝只是拷贝一层,更深层次的对象级别的只拷贝引用(地址)
ES6中新增的语法:Obj.assign(target,..source) 可以用于浅拷贝
参数一:指的是拷贝之后的对象
参数二:被拷贝者,
// 浅拷贝只是拷贝一层,对于深层次的对象级别的只拷贝引用
var obj={
isd:1,
name:'tony',
msg:{
age:18
}
}
var o={};
/*
//方法一
for(var k in obj){
//k 是属性名 obj[k]属性值
o[k]=obj[k]//o[k]相当于o.k
}
console.log(o);
// 拷贝之后的o与原来的obj指向的是同一个地址
// 浅拷贝把更深层次对象的地址拷贝给了o,当修改了age时,原来obj里面的age也会被修改。
o.msg.age=20
console.log(obj); */
//方法二
// 在ES6中可以利用Obj.assign(target,..source) 拷贝
Object.assign(o,obj);
(2)深拷贝
深拷贝拷贝多层,每一级别的数据都会拷贝。深拷贝会新开辟一个容纳数据的空间,也就是复制原来的空间,然后在新空间上修改数据的时候就不会影响原来空间里面的数据。
深拷贝也利用了递归原理
// 浅拷贝只是拷贝一层,对于深层次的对象级别的只拷贝引用
var obj={
isd:1,
name:'tony',
msg:{
age:18
},
color:['pink','red']
}
var o={};
// 封装函数
function deepCopy(newobj,oldobj) {
for (var k in obj){
// 要判断属性值的数据类型
// 判断数据类型要将数组放在对象前面,因为数组也是对象,如果放在后面就会判断不准确
// 1.获取属性值 oldobj[k]
var item=oldobj[k]
// 2.判断这个值是否为数组
if(item instanceof Array){
newobj[k]=[]//这就相当于这个数组为o.color=[]
// 这个函数就是把值给属性,item是值,newobj[i]为属性
deepCopy(newobj[k],item)
}else if(item instanceof Object){
// 3.判断这个值是否为对象
newobj[k]={}
deepCopy(newobj[k],item)
}else{
// 4.属于简单数据类型
newobj[k]=item
}
}
}
deepCopy(o,obj)
console.log(o);
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)