您的位置:首页 > Web前端 > JavaScript

javascript 函数和作用域(函数,this)(六)

2017-02-14 17:53 423 查看
重点。

一、函数

1、函数介绍

函数是一块JavaScript代码,被定义一次,但可执行和调用多次。JS中的函数也是对象,所以JS函数可以像其他对象那样操作和传递,所以我们也常叫JS中的函数为函数对象。

function outer(){
console.log(this===window);
function inner(){
console.log("内部函数:"+(this===window));
}
inner();
}


View Code



倘若语言设计正确,当内部函数被调用时,this应该仍然绑定到外部函数的this变量。这个设计错误的后果就是方法不能利用内部函数来帮助它工作,因为内部函数的this被绑定了错误的值,所以不能共享该方法对对象的访问权。

一个简单的解决方案如下:方法定义一个一个变量that并给它赋值为this,那么内部函数就可以通过that访问到this。

<script>
//函数字面量创建add
var add=function(a,b){
return a+b;
}
var myObject = {
value: 0,
increment: function(inc) {
this.value += typeof inc === 'number' ? inc : 1;
}
}

myObject.increment();
document.writeln(myObject.value); //1

myObject.increment(2);
document.writeln(myObject.value); //3

//给myObject增加一个double方法
myObject.double=function(){
var that=this;//解决方法

var helper=function(){
that.value=add(that.value,that.value);
}
helper();  //以函数的形式调用helper
}

//以方法的形式调用double
myObject.double();
document.writeln(myObject.value); //6
</script>


3、作为对象方法的 函数的this【重点】

只要将函数作为对象的方法o.f,this就会指向这个对象o。

var o={
prop:37,
f:function(){
return this.prop;
}
}

console.log(o.f()); //37


或者

var o={prop:37};
function independent(){
return this.prop;
}

o.f=independent;
console.log(o.f()); //37


在方法调用模式中this到对象的绑定发生在调用时,这个“超级”延迟绑定(very late binding)使得函数可以对this高度重用。【update20170306】

4、对象原型链上的this

对象o有个属性f。

p是个空对象,并且p的原型指向o。给p添加2个属性a和b,再调用p.f()。

调用p.f()的时候调用的是p的原型o上面的这样一个属性f。所以对象原型链上的this调用时指向的还是对象。

var o={f:function(){return this.a+this.b}};

var p=Object.create(o);
p.a=1;
p.b=4;

console.log(p.f()); //5


5、get/set方法与this

get set方法中的this一般也是指向get,set方法所在的对象。

function modulus(){
return Math.sqrt(this.re*this.re+this.im*this.im);

}

var o={
re:1,
im:-1,
get phase(){
return Math.atan2(this.im,this.re);
}
}

Object.defineProperty(o,'modulus',{
get:modulus,
enumerable:true,
configurable:true
})

console.log(o.phase,o.modulus); //-0.7853981633974483 1.4142135623730951


6、构造器中的this【重点】

如果在一个函数前面带上new来调用,那么背地里将会创建一个连接到该函数的prototype成员的新对象,同时this会绑定到那个新对象上。

将MyClass作为了构造器来用。

function MyClass(){
this.a=37;
}

var o=new MyClass();
/*this指向空对象,并且这个空对象的原型指向MyClass.prototype,
this作为返回值,因为没有return
所以对象o就会有属性a为37*/
console.log(o.a);//37


注意:

return语句返回的是对象的话,将该对象作为返回值,所以下面a就是38。

function C2(){
this.a=37;
return {a:38};
}
o=new C2();
console.log(o.a);//38


7、call/apply方法与this【重点】

function add(c,d){
console.log(this.a+this.b+c+d);
}

var o={a:1,b:3};

//call调用
add.call(o,5,7);//16   //1+3+5+7=16
//apply调用
add.apply(o,[10,20]);//34   //1+3+10+20=34


应用

function bar(){
console.log(Object.prototype.toString.call(this));
}

bar.call(7); //[object Number]




call和apply如果this传入null或者undefined的话,this会指向全局对象,在浏览器里就是window。

如果是严格模式的话:

传入this为null和undefined,那this就是null和undefined。



8、bind与this[ES5提供,IE9+才有]

想把某一个对象作为this的时候,就传进去。

function f(){
return this.a;
}

var g=f.bind({a:"test"});
console.log(g());//test
/*
绑定一次,多次调用,仍然实现这样一个绑定,比apply和call更高效
*/

var o={a:37,f:f,g:g};
/*f属性赋值为直接的f方法,g赋值为刚才绑定之后的方法*/
console.log(o.f(),o.g());  //37 "test"
/*o.f()通过对象的属性的方式调用的,返回37*/

/*比较特殊的一点,使用bind方法绑定了之后,即使把新绑定之后的方法作为对象的属性去调用,仍然会按照之前的绑定去走,所以仍然返回test*/


应用【bind的经典例子】

this.x=9; //相当于window.x=9

var module={
x:81,
getX:function(){return this.x;}
};

module.getX();//81 作为对象方法调用

var getX=module.getX();//把对象的方法赋值给一个变量
getX();//9  this指向window,调用的是window的x

var boundGetx=getX.bind(module);
boundGetx();//81  通过bind修改运行时的this


四、函数属性arguments

foo.length拿到形参的个数。在函数内和函数外都有效。foo.length===arguments.callee.length【20170501】

arguments.length拿到实际传参的个数。

arguments.callee当前正在执行的函数

foo.name拿到函数名。

:尝试通过arguments[2]=100修改未传入的z的值,z还是undefined。

就是说:参数如果没传进来的话,arguments和参数没有改下修改这样的绑定关系。

function foo(x,y,z){
console.log(arguments.length);  //2
console.log(arguments[0]); //1

arguments[0]=10;
console.log(x);   //有绑定关系,形参x被修改为10

arguments[2]=100;//z未传入
console.log(z);//没有绑定关系,z仍然是undefined

console.log(arguments.callee===foo);//true,严格模式禁止使用
}

foo(1,2);
console.log(foo.length);//3
console.log(foo.name);//"foo"


好处:使得编写一个无须指定参数个数的函数成为可能。

注意设计错误

因为语言设计错误,arguments并不是一个真正的数组。它只是一个"类似数组(array-like)"的对象。arguments拥有一个length属性,但arguments没有任何数组的方法。

五、bind和函数柯里化

函数柯里化就是把一个函数拆成多个单元。

1、柯里化

function add(a,b,c)    {
console.log(a+'|'+b+'|'+c);
console.log(a+b+c);
}

//不需要改变this,所以传入一个undefined就可以了
var func=add.bind(undefined,100);
func(1,2);
//100|1|2
//103

var func2=func.bind(undefined,200);
func2(10);
//100|200|10
//310


100固定赋值给a参数。

再柯里化一次,200固定赋值给b参数。

2、实际例子

/*getConfig获取一套配置
在不同的页面中配置可能是不一样的,
但是在同一个模块下,可能前2个参数,或者某些参数是一样的

*/
function getConfig(colors,size,otherOptions){
console.log(colors,size,otherOptions);
}

/*this无所谓,写个null或者undefined都可以,可能在某个模块下color都是#CC0000,size都是1024*768*/
var defaultConfig=getConfig.bind(null,"#CC0000","1024*768");

/*拿到defaultConfig这样一个模块级别的通用配置以后,只要传入最后一个参数,可能是每个页面下的单独配置*/

defaultConfig("123");  //#CC0000 1024*768 123
defaultConfig("456");  //#CC0000 1024*768 456


六、bind和new

用new去调用,在this这个层面上.bind()的作用会被忽略。

用new的时候,即使绑定了bind,也会被忽略。



func()直接调用,this会指向bind参数{a:1},return this.a就会返回1.

执行了this.b=100其实是给{a:1}加了个b属性,最后是{a: 1, b: 100}只是不会作为返回值,因为指定了返回值。

new调用的话,return除非是对象,不是对象的话会把this作为返回值,并且this会被初始化为默认的一个空对象,这个对象的原型是foo.prototye。

所以这里new func()调用的时候,即使我们指定了bind方法,this仍然会指向没有bind时所指向的空对象,空对象的原型指向foo.prototype,这个空对象的b属性被设置为100,整个对象会作为一个返回值返回,会忽略return this.a。所以用new func()调用后会返回对象字面量{b:100}。

七、bind方法模拟

在老的浏览器里怎样实现bind方法?模拟实现。

bind方法实现2个功能,绑定this柯里化

MDN的模拟实现。

本文作者starof,因知识本身在变化,作者也在不断学习成长,文章内容也不定时更新,为避免误导读者,方便追根溯源,请诸位转载注明出处:http://www.cnblogs.com/starof/p/6396450.html有问题欢迎与我讨论,共同进步。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: