js自学笔记(3)

1.函数定义

在JavaScript中,定义函数的方式如下:

function abs(x) {
    if (x >= 0) {
        return x;
    } else {
        return -x;
    }
}
如果没有return语句,函数执行完毕后也会返回结果,只是结果为undefined
var abs = function (x) {
    if (x >= 0) {
        return x;
    } else {
        return -x;
    }
};
上述两种定义完全等价,注意第二种方式按照完整语法需要在函数体末尾加一个;,表示赋值语句结束。

2.参数

JavaScript允许传入任意个参数而不影响调用,因此传入的参数比定义的参数多或者少也没有问题

   要避免收到undefined,可以对参数进行检查:

function abs(x) { if (typeof x !== 'number') { throw 'Not a number'; }
}

  JavaScript还有一个关键字arguments,它只在函数内部起作用,并且永远指向当前函数的调用者传入的所有参数arguments类似Array但它不是一个Array

function foo(x) { alert(x); // 10 for (var i=0; i<arguments.length; i++) { alert(arguments[i]); // 10, 20, 30 } } foo(10, 20, 30);

实际上arguments最常用于判断传入参数的个数。你可能会看到这样的写法:

// foo(a[, b], c)
// 接收2~3个参数,b是可选参数,如果只传2个参数,b默认为null:
function foo(a, b, c) { if (arguments.length === 2) { // 实际拿到的参数是a和b,c为undefined c = b; // 把b赋给c b = null; // b变为默认值 } // ... }
为了获取除了已定义参数ab之外的参数,我们可以用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 ] foo(1); // 结果: // a = 1 // b = undefined // Array [] 

rest参数只能写在最后,前面用...标识,从运行结果可知,传入的参数先绑定ab多余的参数以数组形式交给变量rest,所以,不再需要arguments我们就获取了全部参数。

如果传入的参数连正常定义的参数都没填满,也不要紧,rest参数会接收一个空数组(注意不是undefined)。

3.return

JavaScript引擎有一个在行末自动添加分号的机制,这可能让你栽到return语句的一个大坑:

function foo() { return { name: 'foo' }; } foo(); // { name: 'foo' } 

如果把return语句拆成两行:

function foo() { return { name: 'foo' }; } foo(); // undefined 

要小心了,由于JavaScript引擎在行末自动添加分号的机制,上面的代码实际上变成了:

function foo() { return; // 自动添加了分号,相当于return undefined; { name: 'foo' }; // 这行语句已经没法执行到了 } 

所以正确的多行写法是:

function foo() { return { // 这里不会自动加分号,因为{表示语句尚未结束 name: 'foo'  }; }
4.
由于JavaScript的函数可以嵌套,此时,内部函数可以访问外部函数定义的变量,反过来则不行:
'use strict';

function foo() { var x = 1; function bar() { var y = x + 1; // bar可以访问foo的变量x! } var z = y + 1; // ReferenceError! foo不可以访问bar的变量y! }

JavaScript的函数在查找变量时从自身函数定义开始,从“内”向“外”查找。
如果内部函数定义了与外部函数重名的变量,则内部函数的变量将“屏蔽”外部函数的变量
5.变量提升:

JavaScript的函数定义有个特点,它会先扫描整个函数体的语句,把所有申明的变量“提升”到函数顶部

'use strict';

function foo() { var x = 'Hello, ' + y; alert(x); var y = 'Bob'; } foo(); 

虽然是strict模式,但语句var x = 'Hello, ' + y;并不报错,原因是变量y在稍后申明了。但是alert显示Hello, undefined,说明变量y的值为undefined。这正是因为JavaScript引擎自动提升了变量y的声明,但不会提升变量y的赋值。

对于上述foo()函数,JavaScript引擎看到的代码相当于:

function foo() { var y; // 提升变量y的申明 var x = 'Hello, ' + y; alert(x); y = 'Bob'; } 

由于JavaScript的这一怪异的“特性”,我们在函数内部定义变量时,请严格遵守“在函数内部首先申明所有变量”这一规则。

最常见的做法是用一个var申明函数内部用到的所有变量:

function foo() { var x = 1, // x初始化为1 y = x + 1, // y初始化为2 z, i; // z和i为undefined // 其他语句: for (i=0; i<100; i++) { ... } }
6.

全局作用域

不在任何函数内定义的变量就具有全局作用域。实际上,JavaScript默认有一个全局对象window,全局作用域的变量实际上被绑定到window的一个属性:

'use strict';

var course = 'Learn JavaScript';
alert(course); // 'Learn JavaScript' alert(window.course); // 'Learn JavaScript' 

因此,直接访问全局变量course和访问window.course是完全一样的。

你可能猜到了,由于函数定义有两种方式,以变量方式var foo = function () {}定义的函数实际上也是一个全局变量,因此,顶层函数的定义也被视为一个全局变量,并绑定到window对象

'use strict';

function foo() { alert('foo'); } foo(); // 直接调用foo() window.foo(); // 通过window.foo()调用

7.

我们在for循环等语句块中是无法定义具有局部作用域的变量的:

'use strict';

function foo() { for (var i=0; i<100; i++) { // } i += 100; // 仍然可以引用变量i } 

为了解决块级作用域,ES6引入了新的关键字letlet替代var可以申明一个块级作用域的变量

'use strict';

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

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

'use strict';

const PI = 3.14;
PI = 3; // 某些浏览器不报错,但是无效果! PI; // 3.14

8.


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

如果我们给xiaoming绑定一个函数,就可以做更多的事情。比如,写个age()方法,返回xiaoming的年龄:

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了


function getAge() { var y = new Date().getFullYear(); return y - this.birth; } var xiaoming = { name: '小明', birth: 1990, age: getAge }; xiaoming.age(); // 25, 正常结果 getAge(); // NaN 

单独调用函数getAge()怎么返回了NaN请注意,我们已经进入到了JavaScript的一个大坑里。

JavaScript的函数内部如果调用了this,那么这个this到底指向谁?

答案是,视情况而定!

如果以对象的方法形式调用,比如xiaoming.age(),该函数的this指向被调用的对象,也就是xiaoming,这是符合我们预期的。

如果单独调用函数,比如getAge(),此时,该函数的this指向全局对象,也就是window

 
 9.

一个函数接收另一个函数作为参数,这种函数就称之为高阶函数

一个最简单的高阶函数:

function add(x, y, f) { return f(x) + f(y); } 

当我们调用add(-5, 6, Math.abs)时,参数xyf分别接收-56和函数Math.abs。

10.map

由于map()方法定义在JavaScript的Array中,我们调用Arraymap()方法,传入我们自己的函数,

就得到了一个新的Array作为结果:

function pow(x) { return x * x; } var arr = [1, 2, 3, 4, 5, 6, 7, 8, 9]; arr.map(pow); // [1, 4, 9, 16, 25, 36, 49, 64, 81] 

map()传入的参数是pow,即函数对象本身。

我们能一眼看明白“把pow(x)作用在Array的每一个元素并把结果生成一个新的Array”。

所以,map()作为高阶函数,事实上它把运算规则抽象了,因此,我们不但可以计算简单的f(x)=x2,还可以计算任意复杂的函数,

比如,把Array的所有数字转为字符串:

var arr = [1, 2, 3, 4, 5, 6, 7, 8, 9]; arr.map(String); // ['1', '2', '3', '4', '5', '6', '7', '8', '9'] 

只需要一行代码。

11.reduce

Array的reduce()把一个函数作用在这个Array[x1, x2, x3...]上,这个函数必须接收两个参数reduce()把结果继续和序列的下一个元素做累积计算,其效果就是:

[x1, x2, x3, x4].reduce(f) = f(f(f(x1, x2), x3), x4)

比方说对一个Array求和,就可以用reduce实现:

var arr = [1, 3, 5, 7, 9]; arr.reduce(function (x, y) { return x + y; }); // 25

12.filter

filter也是一个常用的操作,它用于把Array的某些元素过滤掉,然后返回剩下的元素

 
 

map()类似,Arrayfilter()也接收一个函数。和map()不同的是,filter()把传入的函数依次作用于每个元素,

然后根据返回值是true还是false决定保留还是丢弃该元素

 
 

在一个Array中,删掉偶数,只保留奇数,可以这么写:

 
 
var arr = [1, 2, 4, 5, 6, 9, 10, 15]; var r = arr.filter(function (x) { return x % 2 !== 0; }); r; // [1, 5, 9, 15]

通常我们仅使用第一个参数,表示Array的某个元素。

回调函数还可以接收另外两个参数,表示元素的位置和数组本身:

var arr = ['A', 'B', 'C']; var r = arr.filter(function (element, index, self) { console.log(element); // 依次打印'A', 'B', 'C' console.log(index); // 依次打印0, 1, 2 console.log(self); // self就是变量arr return true; });
 
 

利用filter,可以巧妙地去除Array的重复元素:

'use strict';

var
    r,
    arr = ['apple', 'strawberry', 'banana', 'pear', 'apple', 'orange', 'orange', 'strawberry'];

 

r = arr.filter(function (element, index, self) {
return self.indexOf(element) === index;
});

去除重复元素依靠的是indexOf总是返回第一个元素的位置,后续的重复元素位置与indexOf返回的位置不相等,因此被filter滤掉了。

13.sort

Arraysort()方法默认把所有元素先转换为String再排序,结果'10'排在了'2'的前面

 

要按数字大小排序,我们可以这么写:

 

var arr = [10, 20, 1, 2]; arr.sort(function (x, y) { if (x < y) { return -1;//x<y则返回-1表示不用sort(从小到大) } if (x > y) { return 1; } return 0; }); // [1, 2, 10, 20] 

 

如果要倒序排序,我们可以把大的数放前面:

 

var arr = [10, 20, 1, 2]; arr.sort(function (x, y) { if (x < y) { return 1; } if (x > y) { return -1; } return 0; }); // [20, 10, 2, 1] 

 

默认情况下,对字符串排序,是按照ASCII的大小比较的,现在,我们提出排序应该忽略大小写,按照字母序排序。

要实现这个算法,不必对现有代码大加改动,只要我们能定义出忽略大小写的比较算法就可以:

 

var arr = ['Google', 'apple', 'Microsoft']; arr.sort(function (s1, s2) { x1 = s1.toUpperCase(); x2 = s2.toUpperCase(); if (x1 < x2) { return -1; } if (x1 > x2) { return 1; } return 0; }); // ['apple', 'Google', 'Microsoft'] 

 

忽略大小写来比较两个字符串,实际上就是先把字符串都变成大写(或者都变成小写),再比较。

sort()方法会直接对Array进行修改,它返回的结果仍是当前Array

 

 



 

转载于:https://www.cnblogs.com/EganZhang/p/6515612.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值