JavaScript 函数进阶

本文详细介绍了ES5新增的数组、字符串和对象方法,深入探讨了函数的多种定义方式、this指向规则及如何改变this指向,同时讲解了闭包的概念与应用,递归函数的实现,以及浅拷贝和深拷贝的区别。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

JavaScript 函数进阶

这篇文章主要讲了ES5新增的方法,函数this指向,严格模式,闭包和浅拷贝和深拷贝的知识
如有错误,欢迎前来指正!

ES5 新增方法

  • 下面记了数组方法, 字符串方法, 对象方法

数组方法

  • forEach 方法:(遍历方法)
// 括号里面是回调函数
array.forEach(function(currentValue, index, arr))
  • currentValue:数组当前项的值

  • index:数组当前项的索引

  • arr:数组对象本身

  • 代码示例:

var arr = [1,2,3];
arr.forEach(function(value, index, array) {
    console.log(value);
})

  • fiter 方法:(筛选数组)
  • 返回的是一个新数组
array.filter(function(currentValue, index, arr))
  • 回调函数里面的参数含义和上面的是一样的
  • 和上面的函数的区别就是他返回的是一个新数组
arr.filter(function(value, index, array) {
    return value >= 20;
})

  • some 方法:(数组中元素是否满足指定条件)
  • 返回值是一个布尔值
array.some(function(currentValue, index, arr))
  • 和上面的方法的区别是这个方法返回的是一个布尔值
  • 这个方法是惰性的,找到第一个满足条件就不再往下查找 (需要 return true 才能终止遍历)

字符串方法

  • trim 方法:删除字符串两端的空白字符
  • trim 方法不影响字符串本身,会返回一个新字符串
var str = '  菜鸟小铭  ';
var str1 = str.trim();
console.log(str1);
// 输出 菜鸟小铭

对象方法

  • Object.keys 方法:用于获取对象所有属性名 (和 for in 很像)
  • 返回一个数组
var obj = {
    id: 1,
    name: "noobMing"
}
var arr = Object.keys(obj);

  • Object.defineProperty() 方法:添加新属性或者修改原有属性
Object.defineProperty(obj, prop, descriptor)
  • obj:必须 目标对象

  • prop:必须 添加或者修改的属性的名字

  • descriptor:必须 添加属性的说明 (以对象格式书写)

  • descriptor 的说明:

    • value 设置属性的值 (默认为 undefined)
    • writable 值是否可以重写 (true/false 默认值为false)
    • enumerable 目标属性是否可以被枚举 (true/false 默认值为false)
    • configurable 目标属性是否可以被删除或是否可以再次修改特性 (true/false 默认值为false)
  • 代码示例:

var obj = {
    name: 'noobMing',
    age: 18
}
Object.defineProperty(obj,'age', {
    value: '20',
    // 不允许修改
    writable: false,
    // 不允许遍历出来
    enumerable: false,
    // 不允许这个属性被删除,也不允许再次修改第三个参数的特性
    configurable: false
})

函数进阶

函数的定义

  • 可以使用 function 直接定义函数, 也可以使用函数表达式定义匿名函数, 使用 new Function('参数1', '参数2', '方法体') 来定义函数
// 1. 直接命名函数
function fn() {}
// 2. 匿名函数
var fun = function() {}
// 3. 使用构造函数来定义函数
var f = new Function('name','console.log("hello"+name)');
  • 所有函数都是 Function 的实例对象
    在这里插入图片描述
    - ==这里有一张图==
立即执行函数
  • 定义一个匿名函数,并且自动调用的就叫立即执行函数
  • 立即执行函数书写方式:
(function(){console.log("我是立即执行函数")})()
  • 在JS引擎中规定,如果 function 出现在行首,一律解析成语句。所以我们只需要让 function 不在行首就可以了 (例如在function前面加上-*/之类的符号)
  • 立即执行函数会形成一个单独的作用域,我们可以封装一些临时变量或者局部变量,避免污染全局变量
  • 参考资料:什么是立即执行函数,它有什么作用?

函数中 this 指向问题

  • 函数中 this 指向:
函数类型this的指向
普通函数(定义在全局的函数)window
定义在对象中的方法指向该对象
构造函数指向创建的实例对象 (包括 prototype 原型对象指向的也是实例对象)
绑定事件的函数指向绑定事件对象 (例如 button 等)
定时器函数window
立即执行函数window
  • this 在函数中永远指向最后调用它的对象,所以不能只看这个函数的定义,还需要看这个函数的执行时指向的是哪里
  • 例如:
var o = {
    a: 10;
    fn: function() {
        console.log(this);
    }
}

// 指向 o
o.fn();
// 这里指向的是 window
var j = o.fn;
j();
  • 第一个是 o 对象直接调用这个函数,所以指向的是 o
  • 第二个的完整写法是 window.j(),this 指向最后调用它的对象也就是 window
改变函数中的 this 指向
  • 可以通过继承时候的用的 call 函数改变
函数名.call(this要指向的地方, 参数);
  • call 可以调用函数,也可以改变函数中 this 的指向
apply 方法改变 this 指向
  • apply 也可以调用和改变函数中 this 指向
fn.apply(thisArg,[argArray]);
  • thisArg:在函数运行时的 this 值
  • argArray:传递的值,但必须包含在数组里
  • 函数接收的部分可以不是一个数组,但参数个数要对的上
  • 例如利用 apply 来计算数组中最大的元素
var arr = [1, 2, 3];
// 这里最好把指向改为函数的调用者 也就是 Math
// var max = Math.max.apply(null, arr);
var max = Math.max.apply(Math, arr);
console.log(max);
// 输出 3
  • apply 应用:可以让数组作为参数去执行一些函数 (例如 max,min)
bind 方法改变函数中 this 指向
  • 这个在实际运用中用的比较多
  • bind 也可以改变 this 指向,但是不会调用这个函数
fun.bind(thisArg, arg1, arg2);
  • thisArg:在函数运行时的 this 值

  • arg:传递的值

  • 返回改变 this 之后的原函数的拷贝

  • 代码示例:

// 新建一个对象 o
var o = {
    name: "菜鸟小铭"
};
// 新建一个函数 fn
function fn(a,b) {
    console.log(this);
    console.log(a+b);
};
var f = fn.bind(o, 1, 2);
// 因为 bind 不立即执行,所以需要自己调用
// 即使在此处修改参数也不会改变相加的值
f(3,4);
// 输出对象 o 和 3

  • 三个函数的相同点和不同点:
    - ==这里有一张图==

严格模式

  • 严格模式清除了一些不合理的地方 (毕竟是一个星期就创造出的语言),并且新增了一些保留字
  • 有兼容性问题:IE10 以上 (旧版本浏览器会忽略)

开启严格模式

  • 严格模式可以应用到整个脚本或者个别函数中
  • 为脚本开启严格模式:
// 下面的代码就会按照严格模式运行
'use strict';
  • 为函数开启严格模式:
// 为这个函数开启严格模式
function fn() {
    'use strict';
}

严格模式的变化

  • 严格模式下变量必须先用 var 声明在使用 (非严格模式下变量没有声明就赋值,默认是全局变量)
'use strict';
// 报错,变量未定义
num = 10;
// 正确写法
var num = 10;
  • 不能在严格模式下删除已经定义好的变量
var num = 10;
delete num;
// 报错,不能删除已经好的变量
  • 严格模式下全局函数里的 this 指向 undefined
function() {
    // undefined
    console.log(this);
}
  • 构造函数不加 new 调用,this 就会报错 (因为全局函数指向的是 undefined,不能给 undefined 加属性)

  • 严格模式下函数不能有重名的参数 (好像没有人会这么用吧)

  • 不允许在 if for 等非函数代码块里面声明函数

  • 更多严格模式的内容

高阶函数

  • 接受函数作为参数 (回调函数) 或者 将函数作为返回值输出 都叫高阶函数
  • 在JavaScript中,函数都是以对象的形式储存的,所以可以让函数作为参数来使用

闭包

  • 闭包是一个作用域可以访问另外一个函数的局部变量
  • 最简单的闭包:
function fn() {
    var num = 10;
    function fun() {
        console.log(num);
    }
    fun();
}
// fun 访问 fn 作用域里的局部变量,就产生了闭包 (Closure)

闭包的作用

  • 闭包延伸了局部变量的范围
  • 利用 return 来扩大变量作用范围
function fn() {
    var num = 10;
    return function() {
        console.log(num);
    }
}
var f = fn();
f();
// 输出 10 (在全局作用域下可以访问到 fn 内部的变量)

闭包的应用

  • 给一堆 li 绑定点击事件,点击哪个 li 获取哪个 li 的索引号
  • 原来的做法:先给 li 设置自定义属性里面写上标号,然后再点击的时候获取标号
  • 现在的做法:立即执行函数生成了四个作用域,每一个作用域里的 onclick 事件都对应这不同的标号 (i 值不同)
var lis = document.querySelectorAll('li');
for (var i = 0; i < lis.length; i++) {
    (function (i) {
        lis[i].onclick = function () { console.log(i);}
    })(i);
}

递归函数

  • 递归:自己调用自己
  • 先写一个简单的阶乘
function factorial(num) {
    if (num <= 1) return 1;
    else {
        return factorial(--num) * num;
    }
}
console.log(factorial(10));
// 输出 362880
  • 一定要小心栈溢出 (stack overflow)

浅拷贝和深拷贝

  • 浅拷贝只是拷贝一层,更深层次 对象级别 的只拷贝引用

  • 深拷贝拷贝多层,每一级别的数据都会拷贝

  • 两者的区别:

    • 浅拷贝的对象级别成员拷贝时只传递一个地址,导致两个拷贝前后一个改变另一个也跟着改变
    • 深拷贝是两个对象成员指向了两个不同的地址,所以即使改变也不会影响其他地方
浅拷贝
var source = {
    name:"菜鸟小铭",
    msg: {
        data: "用代码构建世界!"
    }
};
var copy = {};
// 浅拷贝
for(var k in obj) {
    // k 是属性名,obj[k] 是属性名
    copy[k] = source[k];
}
console.log(copy);
// 输出的内容和 source 是一摸一样的
// 浅拷贝的对象内容指向的是一个地址
// 但是我们现在改变 copy 中的 data,source 也跟着一起改变
copy.msg.data = "Never Settle";
// 这时发现两个内容一起改变
  • ES6 新增的浅拷贝语法糖:

语法糖:语法糖就是一种便捷写法,使代码更简洁流畅,代码更语义自然

Object.assign(target,source);
  • target:要拷贝给谁
  • source:要拷贝哪个对象
深拷贝
  • 深拷贝使用递归函数来把所有内容都给放进去
function deepcopy(newobj, oldobj){
    for(var k in oldob3) {
    // 判断我们的属性值属于那种数据类型
    // 1.获取属性值 oldobj[k]
    var item= oldobj[k]// 2.判断这个值是否是数组
    if(item instanceof Array) {
        newobj[k] = [];
        deepCopy(newobj[k],item);
    } else if (item instanceof object) {
        // 3.判断这个值是否是对象
        newobj[k]={};
        deepCopy(newobj[k],item);
    } else {
        //4.属于简单数据类型
        newobj[k]= item;
        }
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值