对象隐式转换
/*对象通过toString()和toLocaleString()隐式转换成字符串,通过valueOf隐式转换成数值;
在进行强转字符串类型时将优先调用toString方法,强转为数字时优先调用valueOf。
*/
"js"+{toString:function(){return "123";}} //js123
1=={valueOf:function(){return true;}} //true;
var color=["red","green","blue"];
color.toString();//"red,green,blue"
color.valueOf();//["red","green","blue"]
闭包
定义:有权访问在另一函数作用域内变量的函数叫做闭包;
特性:当一个函数返回它内部定义的一个函数时,就产生了一个闭包,闭包不但包括被返回的函数,还包括这个函数的定义环境。
闭包的三个特点:
- 闭包允许当前函数使用在外部函数定义的变量
//函数可以引用定义在其外部作用域的变量
function makeSandwich(){
var magicIngredient="peanuts butter";
function make(filling){
return magicIngredient+ " and "+filling;
}
return make("jelly");
}
makeSandwich(); //"peanuts butter and jelly"
- 即使外部函数已返回,当前函数仍然能使用外部函数定义的变量
//闭包比创建它们的函数有更长的生命周期
function makeSandwich(){
var magicIngredient="peanuts butter";
function make(filling){
return magicIngredient+ " and "+filling;
}
return make;
}
//或者
function makeSandwich(){
var magicIngredient="peanuts butter";
return function make(filling){
return magicIngredient+ " and "+filling;
};
}
var f = makeSandwich();
f("apple");//"peanuts butter and apple"
f("bananas");//"peanuts butter and bananas"
解释:JS的函数值包含了比调用它们时执行所需要的代码还要多的信息,它们在内部存储着可能会引用的定义在封闭作用域的变量。
- 闭包可以更新外部变量的值
闭包实际存储的是对外部变量的引用,而不是它们的副本,因此可以对其进行更新
//函数在内部存储其外部变量的引用,并能读写这些变量
//2D storage.js
var wrapStorage = function () {
lo = {};
return {
get: function (attr) {
return lo[attr];
},
set: function (attr, val) {
lo[attr] = val;
return this;
},
remove: function (attr) {
delete lo[attr];
return this;
}
size: function () {
return Object.keys(lo).length;
},
toJSON: function () {
var o = {}, i;
for (i in lo) {
o[i] = lo[i];
}
return o;
},
toString: function () {
return JSON.stringify(lo);
}
};
};
var jsonObject=wrapStorage("name");
jsonObject.get(1);//undefined
jsonObject.set(1,"haha");//undefined
jsonObject.get(1);//"haha"
变量声明提升
函数内部的变量只有函数级作用域,没有块级作用域;
理解变量提升:变量的声明与赋值是分开的,当用var 声明并赋值一个变量时,JS隐式的将变量的声明提升到函数的顶部,并赋值留在原处;
使用立即调用函数创建局部作用域
//在运行时进入作用域,JS会为每个在作用域内的变量分配一个"slot"
function wrapElements(a){
var i,result = []; //分配了两个"slot"
for(i=0;i<a.length;i++){
result[i]=function(){ //闭包存储的是对外部变量i的引用而不是i的值,因此每个闭包其实都引用一个共享的"slot",即5(i的值最后为5)
return a[i];
}
}
return result;
}
var wrapped=wrapElements([1,2,3,4,5]);
var f=wrapped[0];
f();//undefined
//解决办法:创建一个嵌套函数并立即调用来创建一个局部作用域
function wrapElements(a){
var i,result = [];
for(i=0;i<a.length;i++){
(function(j){
result[i]=function(){
return a[j];
}
})(i);//每次执行都会创建一个新的"slot"
}
return result;
}
var wrapped=wrapElements([1,2,3,4,5]);
var f=wrapped[0];
f();//1
call:使用call来自定义接收者来调用方法
- 自定义接收者来调用函数,原始方法
obj.temporary=f;
var result=obj.temporary(arg1,arg2,arg3);
delete obj.temporary;
缺点:代码复杂,而且通常情况下不应该修改obj对象;
- call方法
f.call(obj,arg1,arg2,arg3); //obj即为接收者对象
//实例
var dict={};
dict.foo=1;
var f=function(arg){
return arg?arg:this.foo; //执行call时,this就是dict对象
};
f.call(dict);//1
- 总结:
- 使用call方法来自定义接收者来调用方法;
- 使用call方法可以调用在给定对象中不存在的方法(继承);
apply:使用apply通过不同数量的参数调用函数
//apply方法与call方法作用一样,但是apply方法的第二个参数是一个数组
f.apply(obj,[arg1,arg2,arg3]);
arguments
- JS给每个函数都隐式的提供了一个arguments变量,arguments对象有一个length属性来指示参数的个数
- 永远不要修改arguments对象
//arguments对象不是函数参数的副本,而且所有命名参数都是arguments对象中对应索引的别名。
function callMethod(obj,method){
Array.prototype.shift.call(arguments);
Array.prototype.shift.call(arguments);//即使删除元素,obj仍然为arguments[0]的别名;method仍为arguments[1]的别名;
return obj[method].apply(obj,arguments);//因此此时,method==arguments[0]==3,obj==arguments[1]==4,
}
var obj={add:function(x,y){return x+y ;}};
callMethod(obj,"method",3,4);
//非严格模式下
function nonstrict(x){
arguments[0]="123";
console.log("x = "+x); //x="123"
console.log("arguments[0] = "+arguments[0]); //arguments[0]="123"
return x===arguments[0]; //true
}
nonstrict("456");
//严格模式下
function strict(x){
"use strict";
arguments[0]="123";
console.log("x = "+x);//x="456"
console.log("arguments[0] = "+arguments[0]);//arguments[0]="123"
return x===arguments[0];//false
}
var args = Array.prototype.slice.call(arguments)将arguments对象复制到一个真正的数组中再进行操作
数组方法
- concat : //叠加数组
var color=["red","green","blue"];
var tempColor=color.concat("yellow",["black,white"]);
alert(color); //["red","green","blue"];
alert(tempColor);//["red","green","blue","yellow","black","white"];
//如果concat或slice后面的参数为空,则相当于复制了一个副本到新的数组
- slice : 提取数组
var color=["red","green","blue","yellow"];
var color2=color.slice(1); //["green","blue","yellow"]
var color3=color.slice(1,3); //["green","blue"]
- splice
var color=["red","green","blue"];
//删除:两个参数,要删除的第一项的位置,要删除的项数
var removed=color.splice(0,1); //从位置1开始删除一项
alert(color); //["green","blue"]
alert(removed); //["red"]
//插入:要插入的位置,要删除的项数,要插入的项
removed=color.splice(1,0,"white","black");//从位置1插入两项
alert(color); //["green","white","black","blue"]
alert(removed); //空数组
//替换:要插入的位置,要删除的项数,要插入的项
removed=color.splice(1,1,"red","yellow");//从位置1开始删除一项并插入两项
alert(color); //["green","red","yellow","black","blue"]
alert(removed); //["white"]
- every,some,filter,forEach,map
//函数不支持break和continue,如果需要调到下一个循环则用return,无法退出循环
/*every:每一项都返回true才返回true;
some:只要有一项返回true就返回true;
filter:对数组中的每一项都运行函数,返回该函数会返回true的项组成的数组;
map:对数组中的每一项都运行函数,返回该函数调用结果组成的数组;*/
原型
每个函数都有一个prototype属性,这个属性是一个指针,指向一个(原型)对象,这个对象的用途是包含所有实例共享的属性和方法
原型对象
所有原型对象都会获得一个constructor(构造函数)属性,这个属性包含一个指向prototype属性所在函数的指针:
function person(){};
person.prototype.constructor===person; //true
当用构造函数创建一个实例的时候,这个实例将包含一个属性,它是一个指向构造函数的原型对象的指针。
var man=new person();
man.constructor===person; //true
man.__proto__===person.prototype; //true