掌握Javascript(五)

本文介绍Javascript中函数相关知识。


函数声明和表达式

前面介绍的函数是用函数声明方式

function walk(){

 // …

}

还可以用表达式,注意表达式方式后面有分号。

let walk = function () {

 // …

};

调用:

walk();


提升 Hoisting

函数声明可以在声明之前调用,比如:

walk();

function walk() {

 // …

}

而表达式函数则不可以在定义之前调用。

原因:

javascript引擎会在运行时将所有声明的函数先提到最前面(Hoisting)。


参数

Javascript是动态函数,不一定要输入与形参一样的参数个数。

function sum(a, b) {

 return a + b;

}

// 调用

console.log(sum(1, 2)); //3

console.log(sum(1));

// 输出 NaN,相当于1+undefined

console.log(sum());

// 输出 NaN,相当于undefined+undefined

函数可以获取 arguments数组,该数组存着所有参数。

function sum() {

 let total = 0;

 for (let value of arguments) total += value;

 return total;

}

// 然后调用

console.log(sum(1, 2, 3, 4, 5));

// 输出 15


余下操作符

余下操作符是三个点 …

与拆分操作符的标识一样,现代javascript才有。

function sum(…args) {

 // … 是余下操作符,表示将参数放数组args中

 return args.reduce((a, b) => a + b);

 // 累加

}

console.log(sum(1, 2, 3, 4, 5, 10));

// 输出 25

余下操作符参数必须是最后一个参数。

function sum(discount,…price,someValue){

 // 不最后一个将出错

 //一些操作

}

sum(0.1,20,30,1);

// 提示出错:Rest parameter must be last formal parameter

余下操作符 有用的一个例子:购物车打折

function sum(discount, …price) {

 const total = price.reduce((a, b) => a + b);

 return total * (1 – discount);

}

// 调用:

console.log(sum(0.1, 20, 30));

// 输出 45


参数默认值

参数默认值在ES6及之后支持。

function interest(princal,rate=3.5,years=5){

// 3.5和5是默认值

 return princal*rate/100*years;

}

interest(10000);

在ES6之前,可以用逻辑操作符代替。

function interest(princal, rate, years) {

 rate = rate || 3.5;

 years = years || 5;

 return ((princal * rate) / 100) * years;

}


存取器

用特殊的方法get、set来设置属性

const person = {

 firstName: “chen”,

 lastName: “yongping”,

 // 定义一个属性fullName,设置get和set

 get fullName() {

   return `${person.firstName} ${person.lastName}`;

 },

 set fullName(value) {

   const parts = value.split(” “);

   this.firstName = parts[0];

   this.lastName = parts[1];

 },

};

// 下面调用试试

person.fullName = “John Smith”;

//调用set,注意 fullName是没有小括号的。

实际上,使用Object的方法也可以来定义属性。

Object.defineProperty(this, “fullName”, {

 get: function () {},

 set: function () {},

});


Try和Catch

直接看代码:

try {

 if (true) {

   // 检查某条件

   throw new Error(“some error”);

   // 抛出错误

 }

} catch (e) {

 // 捕获处理

 console.log(e);

}


局部与全局作用域

局部变量只能在代码块内可见,如在 {}内。

而在代码文件中的变量是全局变量,全局可见。

当局部变量与全局变量一样时,局部覆盖全局变量。


Let和Var

var 的缺点:变量的作用域在它的函数内,比如if语句、循环语句中的变量,在代码块外也能访问。

function start() {

 for (let i = 0; i < 5; i++) console.log(i);

 console.log(i);

 // 这句将出错,因为 i 只在for循环作用域

 // 但如果将let改成var,将输出 5

}

start();

在ES6或者说是ES2015之前,只能使用var;

ES6后,出现了let 和 const等关键字。

  • var。属于函数作用域 function-scope。
  • let,const。属于块作用域 block-scope。

var还在一些问题,如:

var color=’red’;

color会被添加到window的属性。window.color 就是 ‘red’

总之,避免使用var


this关键字

this引用了执行当前函数的对象实例。

  • 方法method中,this指向对象。
  • 函数function中,this指向全局,比如window,global。
  • 构造函数中,this指向新建的空对象,new {}。

来看个初涉javascript用户觉得奇怪的例子:

const video = {

 title: “a”,

 tags: [“a”, “b”, “c”],

 showTags() {

   this.tags.forEach(function (tag) {

     console.log(this.title, tag);

     // 这个this实际指向了全局 window,因为在调用函数中,不属这个对象的方法

   });

 },

};

video.showTags();

 // 输出 undefined ‘a’…,而不是 ‘a’ ‘a’,奇怪吧

javascript的this问题,是每个程序员必须了解的。


改变this的值

在代码块中可以事先保存this,后面再使用。以避免奇怪事情发生。以前面的例子,可以先将this保存到变量self。

showTags() {

   const self = this;

   self.tags.forEach(function (tag) {

     console.log(self.title, tag);

     // 这个this实际指向了全局 window,因为在调用函数中,不属这个对象的方法

   });

 },

这样就能输出正确的结果,不会指向window了。

还有其他一些方案:
可以用call,apply,bind传递this。

看下面代码:

function playVideo() {

 console.log(this);

}

// 直接执行 playVideo,将显示window对象,

playVideo();

// 可以通过call,apply,build等方法执行

playVideo.call({ name: “kelemi” }, 1, 2);

playVideo.apply({ name: “kelemi” }, [1, 2]);

playVideo.bind({ name: “kelemi” })();

call和apply方法的不同在于call需一个一个传递,而apply可以将参数放数组里传。

如果将foreach里回调函数 function() 改成箭头函数 =>,那么这里的this自动继承前面的对象了

 showTags() {

   this.tags.forEach((tag) => {

// 改成箭头函数,this就正常指向了

     console.log(this.title, tag);

   });

 },


小结

本文介绍了Javascript中函数的相关知识,尤其是javascript中this的相关要点必须要了解。下一篇计划介绍javascript的面向对象编程。

发表评论

您的邮箱地址不会被公开。 必填项已用 * 标注