一 函数
1.函数存在的原因?为了重复处理有相同规律的"行为"。
2.抽象
1 + 2 + 3 + … +100,记做
二 函数的定义和调用
1.
函数体内部的语句在执行时,一旦执行到return时,函数就执行完毕,将结果返回。
如果没有return语句,函数执行完毕后也会返回结果,只不过结果为undefined.
2.两种定义函数的方式
function abs(x){
if(x >= 0){
return x;
}else{
return -x;
}
}
//JavaScript的函数也是一个对象,因此,上面定义的abs()函数实际上是一个函数对象,而函数名abs可以视为指向该函数的变量
//因此,诞生了第二种定义函数的方式
var abs = function(x){
if(x >= 0){
return x;
}else{
return -x;
}
};
3.调用函数
abs(5);//5
abs(-6);//6
//JavaScript允许传入任意个参数而不影响调用,因此传入的参数比定义的参数多或少都没有问题
abs(5,'yyc');//5
abs(-6,true,null,2);//6
abs();//NaN
-undefined;//NaN
//为了避免接收到undefined,可以对参数进行检查
function abs(x){
if(x !== 'number'){
throw 'x is not a number!';
}
if(x >= 0){
return x;
}else{
return -x;
}
}
abs();
//VM215:3 Uncaught x is not a number!
4.arguments
1.arguments是一个关键字,只在函数内部起作用。
2.作用:指向当前函数的调用者传入的所有参数。
3.类似Array,但不是一个Array
function foo(x){
console.log(x);//1
for(var i = 0;i < arguments.length;i++){
console.log(arguments[i]);//1 2 3 4 5
}
}
foo(1,2,3,4,5);
4.通过arguments,可以获得调用者传入的所有参数,也就是说,即使函数不定义任何参数,还是可以获得参数的值。
function abs(){
if(arguments.length === 0){
return 0;
}
var x = arguments[0];
return x >= 0 ? x : -x;
}
abs();
//0
abs(5);
//5
abs(-6);
//6
5.argumens主要用来判断传入参数的个数。
5.rest参数—只能写在最后,前面用…标识,避免使用arguments获取所有参数,然后剔除函数定义的参数,获得其他实际传入的参数。
//常规
function foo(a,b){
var i,rest = [];
if(arguments.length > 2){
for(var i = 2;i < arguments.length;i++){
rest.push(arguments[i]);
}
}
console.log('a = ' + a);
//a = 1
console.log('b = ' + b);
//b = 2
console.log(rest);
//[3,4,5]
}
foo(1,2,3,4,5);
//使用rest参数
function foo(a,b,...rest){
console.log('a = ' + a);
//a = 1
console.log('b = ' + b);
//b = 2
console.log(rest);
//[3,4,5]
}
foo(1,2,3,4,5);
function foo(a,b,...rest){
console.log('a = ' + a);
//a = 1
console.log('b = ' + b);
//b = undefined
console.log(rest);
//[]
}
foo(1);
function sum(...rest){
console.log(rest);//[1, 2, 3, 4, 5]
}
sum(1,2,3,4,5);
function sum(...rest){
var sum = 0 ;
for(var i = 0;i < rest.length;i++){
sum += rest[i];
}
return sum;
}
sum(1,2,3,4,5);//15
6.return的一个大坑
1.坑形成的原因:JavaScript引擎有一个在行末自动添加分号的机制。
2.跳下去试试坑
function hole(){
return
{name:'yyc'};
}
hole();//undefined
3.正常方式
function hole(){
return {name:'yyc'};
}
hole();
//{name: "yyc"}
4.保险起见
function hole(){
return {
name: 'yyc'
};
}
hole();
//{name: "yyc"}
三 变量作用域
1.不同函数内部的同名变量相互独立,因为用var申明的变量是有作用域的,当在函数中申明一个变量,该变量的作用域仅为该函数的整个函数体。
2.因为JavaScript的函数可以嵌套,此时,内部函数可以访问外部函数的变量,反之不行!
//内访外----OK
function outer(){
var i = 1;
function inner(){
var y = i +2;
console.log(y);//3
}
inner();
}
outer();
//外访内---No!
function outer(){
var i = 1;
function inner(){
var y = i +2;
}
inner();
console.log(y);//VM297:7 Uncaught ReferenceError: y is not defined
}
outer();
3.如果内部函数和外部函数的变量名重名怎么办?
解决之法:JavaScript的函数在查找变量时,先从自身定义函数开始,由内向外。当内部函数定义了与外部函数重名的变量,优先使用内部函数自己的变量。
function outer(){
var i = 1;
function inner(){
var i = 'yyc';
console.log('内部函数中的i: ' + i);
}
inner();
console.log('外部函数中的i: ' + i);
}
outer();
>>>
内部函数中的i: yyc
外部函数中的i: 1
4.变量提升
function foo(){
var x = 'Hello, ' + y;
console.log(x);
var y = 'yyc';
}
foo();
//Hello, undefined
//其实是这样的
function foo(){
var y;//提升变量y的申明
var x = 'Hello, ' + y;
console.log(x);
y = 'yyc';
}
foo();
//x并没有变化,改变的仅仅是y
function foo(){
var x = 'Hello, ' + y;
console.log(x);
var y = 'yyc';
console.log(x);
}
foo();
>>>
Hello, undefined
Hello, undefined
function foo(){
var x = 'Hello, ' + y;
console.log(x);
var y = 'yyc';
x += y;
console.log(x);
}
foo();
>>>
Hello, undefined
Hello, undefinedyyc
//因此想要得到预期的效果:Hello, yyc:
function foo(){
var y = 'yyc';
var x = 'Hello, ' + y;
console.log(x);
}
foo();
>>>
Hello, yyc
5.全局作用域
1.不在任何函数内定义的变量就具有全局作用域。JavaScript默认有一个全局对象window,全局作用域中的变量实际上被绑定成window的一个属性:
var gv = 'learn JavaScript';
gv;
//"learn JavaScript"
window.gv;
//"learn JavaScript"
6.名字空间
1.存在的原因?当几个不同的JavaScript文件使用了同名的全局变量,或者定义了同名的顶层函数,就会造成命名冲突
2.因此将自己的代码全部放入唯一的命名空间yyc中,会大大减少全局变量冲突的可能
var yyc = {};
yyc.name = 'yyc';
yyc.version = 1.0;
yyc.interest = function(){
return 'reading';
};
7.let
1.存在原因?因为JavaScript只有全局作用域和局部作用域(函数内部),没有块级作用域的概念。
function foo(){
var sum = 0;
for(var i = 1;i < 5;i++){
sum += i;
}
console.log(sum);//10
console.log(i);//5
}
foo();
function foo(){
var sum = 0;
//变量i的生命周期仅存在于for循环中
for(let i = 1;i < 5;i++){
sum += i;
}
console.log(sum);//10
console.log(i);//Uncaught ReferenceError: i is not defined
}
foo();
8.常量
const PI = 3.14;
PI = 3;
PI;//Uncaught TypeError: Assignment to constant variable.
四 方法
1.在一个对象中绑定一个函数,成为这个对象的方法。
var Person = {
name: 'ycc',
birth: 1996,
age: function(){
var y = new Date().getFullYear();
return (y - this.birth);
}
};
Person.age();//21
//this是一个特殊的变量,它始终指向当前对象,这里,也就是Person这个对象。
2.this的一个大坑
function getAge(){
var y = new Date().getFullYear();
return (y - this.birth);
}
var Person = {
name: 'ycc',
birth: 1996,
age: getAge
};
Person.age();//21---这里的this指向的是Person对象,因此this.birth = 1996
getAge();//NaN-----因为this指向的是全局对象,因此this.birth = undefined
//验证一下
var birth = 2017;
function getAge(){
console.log(this.birth);
}
getAge();//2017
3.apply—控制this的指向
//常规
function getAge(){
var y = new Date().getFullYear();
return (y - this.birth);
}
var Person = {
name: 'ycc',
birth: 1996,
age: getAge
};
getAge();
//NaN
//使用apply----arug1代表需要绑定的this变量,null即为调用普通函数 ;argu2代表函数本身的参数
function getAge(){
var y = new Date().getFullYear();
return (y - this.birth);
}
var Person = {
name: 'ycc',
birth: 1996,
age: getAge
};
getAge.apply(Person,[]);
//21
Math.max.apply(null,[1,2,3,4,5]);
//5
Math.max.call(null,1,2,3,4,5);
//5
五 高级函数
1.什么是高阶函数?一个函数作为另一个函数的参数。
function add(a,b,f){
return f(a) + f(b);
}
add(5,-6,Math.abs);//11
2.Map | Reduce
1.将函数f(x) = x * x 作用在一个数组[1,2,3,4,5,6]上。
//常规方式
var arr = [1,2,3,4,5,6];
function powArray(arr){
var result = [];
for(var i = 0;i < arr.length;i++){
result.push(arr[i]*arr[i]);
}
return result;
}
powArray(arr);
//[1, 4, 9, 16, 25, 36]
//map----更优雅的方式----对于Array中的每个元素调用pow函数
function pow(x){
return x*x;
}
var arr = [1,2,3,4,5,6];
arr.map(pow);
//[1, 4, 9, 16, 25, 36]
//将Array的所有元素转化为布尔值
var arr = [null,undefined,0,'',NaN];
arr.map(Boolean);
//[false, false, false, false, false]
3.reduce
[x1,x2,x3,x4].reduce(f) = f(f(f(x1,x2),x3),x4)
Array求和
//常规方式
var arr = [1,2,3,4,5];
function summation(arr){
var sum = 0
for(var i = 0;i < arr.length;i++){
sum += arr[i];
}
return sum;
}
summation(arr);
//15
//reduce方式
var arr = [1,2,3,4,5];
function add(x,y){
return x+y;
}
arr.reduce(add);
//15
var arr = [1,2,3,4];
arr.reduce(function(x,y){
return x*10 + y;
});
//1234
4.***JavaScript进行减法运算时,会将前后的值转换为数值再进行运算,若转化失败,返回NaN
'111'-0;
//111
true- 0;
//1
null - 0;
//0
undefined - 0;
//NaN
'' - 0;
//0
//任务:"13579"---->[1,3,5,7,9]---->13579
var s = '13579';
function string2int(s){
function string2Number(x){
return x-0;
}
var intermediate = s.split('').map(string2Number);
function mul(x,y){
return x*10+y;
}
return intermediate.reduce(mul);
}
string2int(s);
//13579
5.输入:[‘adam’, ‘LISA’, ‘barT’],输出:[‘Adam’, ‘Lisa’, ‘Bart’]。
var arr = ['adam', 'LISA', 'barT'];
function normalize(arr){
function adjust(s){
//除了第一个字母其余全小写
var s1 = s.toLowerCase().slice(1);
var result = s[0].toUpperCase() + s1;
return result;
}
return arr.map(adjust);
}
normalize(arr);
>>>
(3) ["Adam", "Lisa", "Bart"]
6.map & parseInt | Number
var arr = ['1', '2', '3'];
arr.map(parseInt);
>>>
(3) [1, NaN, NaN]
why?
1.map()接收三个参数:element | index | array
2.parseInt()接收两个参数:string | radix [2-36]
3.parseInt()方法用于解析一个字符串,并返回一个整数
//等价于
parseInt('1',0);//1----代表默认进制,也就是十进制
parseInt('2',1);//NaN----radix的范围[2,36]
parseInt('3',2);//NaN----二进制只能包含0或1
7.filter
1.是什么?与map()类似,filter()也接收一个函数。
2.与map()区别:
//map()
var arr = [1,2,3,4,5,6];
arr.map(function(x){
return (x % 2 !== 0);
});
//(6) [true, false, true, false, true, false]
//filter----把传入的函数依次作用于每个元素,根据返回值是true/false来决定保留还是删除该元素。
var arr = [1,2,3,4,5,6];
arr.filter(function(x){
return (x % 2 !== 0);//保留奇数
});
//(3) [1, 3, 5]
3.去掉Array: ['A', '', 'B', null, undefined, 'C', ' '];中的空字符串
var arr = ['A', '', 'B', null, undefined, 'C', ' '];
arr.filter(function(x){
//左边的x先排除掉null、undefined和''纯的空字符串,因为这三兄弟Boolean()一下是false
//右边的表达式将' '先转换为纯的空字符串,然后再Boolean一下,也是false
return (x && x.trim());
});
//(3) ["A", "B", "C"]
8.回调函数
1.map() & filter() 回调函数大PK
//map() --- element
var arr = ['yyc',2,true,null];
arr.map(function(element,index,array){
console.log(element);
});
>>>
yyc
2
true
null
//map() --- index
var arr = ['yyc',2,true,null];
arr.map(function(element,index,array){
console.log(index);
});
>>>
0
1
2
3
//map() --- array
var arr = ['yyc',2,true,null];
arr.map(function(element,index,array){
console.log(array);
});
>>>
["yyc", 2, true, null]
["yyc", 2, true, null]
["yyc", 2, true, null]
["yyc", 2, true, null]
//filter() --- element
var arr = ['yyc',2,true,null];
arr.filter(function(element,index,array){
console.log(element);
});
>>>
yyc
2
true
null
//filter() --- index
var arr = ['yyc',2,true,null];
arr.filter(function(element,index,array){
console.log(index);
});
>>>
0
1
2
3
//filter() --- array
var arr = ['yyc',2,true,null];
arr.filter(function(element,index,array){
console.log(array);
});
>>>
["yyc", 2, true, null]
["yyc", 2, true, null]
["yyc", 2, true, null]
["yyc", 2, true, null]
//好吧,事实证明,一样样的
删除一个数组中重复的字符串,如:['apple', 'strawberry', 'banana',
'pear', 'apple', 'orange', 'orange', 'strawberry'];
//常规方法
var arr = ['apple', 'strawberry', 'banana',
'pear', 'apple', 'orange', 'orange', 'strawberry'];
function unique(arr){
var result = [];
for(var i = 0;i < arr.length;i++){
if(result.indexOf(arr[i]) === -1){
result.push(arr[i]);
}
}
return result;
}
unique(arr);
>>>
(5) ["apple", "strawberry", "banana", "pear", "orange"]
//使用Set对象----特性:一个Key只能对于一个value
var arr = ['apple', 'strawberry', 'banana',
'pear', 'apple', 'orange', 'orange', 'strawberry'];
Array.from(new Set(arr));
>>>
(5) ["apple", "strawberry", "banana", "pear", "orange"]
//filter & indexOf----因为indexOf总是返回第一个元素的位置
var arr = ['apple', 'strawberry', 'banana',
'pear', 'apple', 'orange', 'orange', 'strawberry'];
arr.filter(function(element,index,array){
return (array.indexOf(element) === index);
});
>>>
(5) ["apple", "strawberry", "banana", "pear", "orange"]