【JS】JS基础复习笔记——函数基础

本文详细解析了JavaScript中的函数定义与调用,包括函数定义、变量作用域、结构赋值(如解构数组和对象)、方法(this、Apply与Call、装饰器)。理解变量提升、作用域规则和高级特性有助于提升前端开发技能。
摘要由CSDN通过智能技术生成

提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档


前言

本文用于自行复习前端基础知识,来源于现有教程等材料的整理,如有纰漏敬请包涵!
第一部分:函数。


一、函数定义与调用

1.1 定义函数

在Javascript中有两种定义函数的方式:

  1. 第一种:将函数定义为函数对象,函数名可以视为指向函数的变量。
function abs(x){
   if(x>=0){
   	return x;}
   else{
   	return -x;}
}
  1. 第二种:定义的匿名函数没有变量名,将匿名函数赋值给变量abs,每次调用变量abs就是调用该函数。
var abs=function(x){
	if(x>=0){
		return x;
	}else{
		return -x;
	}
}

JavaScript引擎在行末自动添加分号的机制需要格外注意,正确的多行写法如下:

function foo() {
    return { // 这里不会自动加分号,因为{表示语句尚未结束
        name: 'foo'
    };
//错误示范
function foo() {
    return//这里会自动添加分号,相当于没有返回任何内容
        { name: 'foo' };//被忽略
}

foo(); // undefined
}

1.2 调用函数

调用函数时,按顺序传入参数即可。

abs(107);//返回107
abs(-107);//返回-107
abs(-100,'www','keyima');//返回100
abs();//返回NaN

由于JavaScript允许传入任意个参数而不影响调用,因此传入的参数比定义的参数多也没有问题,虽然函数内部并不需要这些参数;如果传入参数比定义的少,则参数 x 将会是 undefined ,计算结果为NaN

1.3 arguments

  • arguments在函数内部可用,并且将会永远指向当前函数的调用者传入的所有参数。
  • arguments类似数组但不是数组。
function abs() {
    if (arguments.length === 0) {
        return 0;
    }
    var x = arguments[0];
    return x >= 0 ? x : -x;
}

1.4 rest

  • ES6标准引入了rest参数;
  • 以数组的形式获取已定义参数以外的其他参数;
  • 如果传入参数数目不足定义参数,rest参数接收到一个空数组。
function foo(a, b, ...rest) {
    console.log('a = ' + a);
    console.log('b = ' + b);
    console.log(rest);
}

foo(1, 2, 3, 4, 5);
// 结果:
// a = 1
// b = 2
// Array [ 3, 4, 5 ]

二、变量作用域与结构赋值

2.1 作用域的基本特点

  • Javascript中用var声明的所有变量都有作用域;
  • 在函数内部声明的变量作用域是整个函数体(函数外不可用);
  • 不同函数名内声明的同名变量互相独立,互不影响;
  • 由于JavaScript的函数可以嵌套,内部函数可以访问外部函数定义的变量,反之则不然;
  • 如果内部函数定义了与外部函数重名的变量,则内部函数的变量将“屏蔽”外部函数的变量。

2.2 变量提升

Javascript函数定义的特点:会先扫描整个函数体的语句,把所有申明的变量“提升”到函数顶部,但只会提升变量的声明,而不会提升变量的赋值(还是undifined)。
因此,在函数内部定义变量时,严格遵守 “在函数内部首先申明所有变量。” 这一规则。
最常见的做法是在函数定义的开头用一个var申明函数内部用到的所有变量。

2.3 全局作用域

  • 不在任何函数内定义的变量就具有全局作用域
  • JavaScript默认有一个全局对象window,全局作用域的变量实际上被绑定到window的一个属性
  • 顶层函数的定义也被视为一个全局变量,并绑定到window对象。
  • 任何对象在被查找时,都是从内向外查找的,逐级向上查找;如果在全局作用域中也没有找到,则报ReferenceError错误

2.4 名字空间

为了避免不同Javascript文件之间的全局变量命名冲突,常用的方法是把自己的所有变量和函数全部绑定到一个全局变量中,如下所示:

// 唯一的全局变量MYAPP:
var MYAPP = {};

// 其他变量:
MYAPP.name = 'myapp';//都是MYAPP的属性
MYAPP.version = 1.0;

// 其他函数:
MYAPP.foo = function () {
    return 'foo';
};

2.5 局部作用域

在语句块内定义具有局部作用域的变量,ES6引入了新的关键字 let,用let替代var可以申明一个块级作用域的变量:

'use strict';

function foo() {
    var sum = 0;
    for (let i=0; i<100; i++) {
        sum += i;
    }
    // SyntaxError:
    i += 1;
}

2.6 常量

ES6标准引入了新的关键字const来定义常量,const与let都具有块级作用域。

2.7 解构赋值

从ES6开始,JavaScript引入了解构赋值,可以同时对一组变量进行赋值。

2.7.1 数组元素

'use strict';
//对数组元素进行解构赋值时,多个变量要用[...]括起来。
var [x, y, z] = ['hello', 'JavaScript', 'ES6'];
//如果数组本身还有嵌套,也可以通过下面的形式进行解构赋值,注意嵌套层次和位置要保持一致:
let [x, [y, z]] = ['hello', ['JavaScript', 'ES6']];
//解构赋值可以忽略某些参数
let [, , z] = ['hello', 'JavaScript', 'ES6']; // 忽略前两个元素,只对z赋值第三个元素

2.7.2 对象属性

此外,获取对象的指定属性也可以用解构赋值:

var person = {
    name: '小明',
    age: 20,
    gender: 'male',
    passport: 'G-12345678',
    school: 'No.4 middle school'
    address: {
        city: 'Beijing',
        street: 'No.1 Road',
        zipcode: '100001'
};
var {name, age, passport} = person;
//name = '小明', age = 20, passport = 'G-12345678'
var {name, address: {city, zip}} = person;
//zip - undefined, 因为属性名是zipcode而不是zip

//进行默认赋值,避免了不存在的属性返回undefined的问题
var {name, single=true} = person;
name; // '小明'
single; // true

//如果要使用的变量名和属性名不一致,可以用下面的语法获取:
// 把passport属性赋值给变量id:
let {name, passport:id} = person;
name; // '小明'
id; // 'G-12345678'
// 注意: passport不是变量,而是为了让变量id获得passport属性:
passport; // Uncaught ReferenceError: passport is not defined

//如果变量已经被声明了,再次赋值的时候,正确的写法也会报语法错误,解决方法是用小括号括起来:
// 声明变量:
var x, y;
// 解构赋值:
({x, y} = { name: '小明', x: 100, y: 200});//注意小括号

2.7.3 使用场景

交换变量:

var x=1, y=2;
[x, y] = [y, x]

快速获取当前页面的域名和路径:

var {hostname:domain, pathname:path} = location;

三、 方法

3.1 关键字this

在一个对象中绑定函数,称为这个对象的 方法

var xiaoming = {
    name: '小明',
    birth: 1990,
    age: function () {
        var y = new Date().getFullYear();
        return y - this.birth;
    }
};

xiaoming.age; // function xiaoming.age()
xiaoming.age(); // 今年调用是25,明年调用就变成26了

//单独调用函数this的指向会出错
var fn = xiaoming.age;
fn(); // Uncaught TypeError: Cannot read property 'birth' of undefined

方法内部使用了一个关键字 this
this :一个特殊变量,它始终指向当前对象,也就是xiaoming这个变量。
注意:obj.xx才能保证this指向正确,如果单独调用函数,this指向全局变量window,strict模式下this指向undefined,均不能得到想要的结果

此外,this指针只在age方法的函数内指向xiaoming,在函数内部定义的函数,this又指向undefined,也就是说函数的嵌套需要用到that变量首先捕获this

var that = this; 

例子如下:

'use strict';

var xiaoming = {
    name: '小明',
    birth: 1990,
    age: function () {
        var that = this; // 在方法内部一开始就捕获this
        function getAgeFromBirth() {
            var y = new Date().getFullYear();
            return y - that.birth; // 用that而不是this
        }
        return getAgeFromBirth();
    }
};

xiaoming.age(); // 25

3.2 Apply与Call

要指定函数的this指向哪个对象,可以用函数本身的apply方法,它接收两个参数,第一个参数就是需要绑定的this变量,第二个参数是Array,表示函数本身需要的参数。

function getAge() {
    var y = new Date().getFullYear();
    return y - this.birth;
}

var xiaoming = {
    name: '小明',
    birth: 1990,
    age: getAge
};

xiaoming.age(); // 25
getAge.apply(xiaoming, []); // 25, this指向xiaoming, 参数为空

另一个与apply()类似的方法是call(),唯一区别是:

  • apply()把参数打包成Array[]再传入;
  • call()把参数按顺序传入。
Math.max.apply(null, [3, 5, 4]); // 5
Math.max.call(null, 3, 5, 4); // 5

对普通函数调用,我们通常把this绑定为null

3.3 装饰器

利用apply(),我们还可以动态改变函数的行为。

JavaScript的所有对象都是动态的,即使内置的函数,我们也可以重新指向新的函数。

现在假定我们想统计一下代码一共调用了多少次parseInt(),可以把所有的调用都找出来,然后手动加上count += 1,不过这样做太傻了。最佳方案是用我们自己的函数替换掉默认的parseInt():

'use strict';

var count = 0;
var oldParseInt = parseInt; // 保存原函数

window.parseInt = function () {
    count += 1;
    return oldParseInt.apply(null, arguments); // 调用原函数
};
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值