函数和作用域
什么是函数?
函数是一块javascript代码,被定义一次,可执行和调用多 次。Js中的函数也是对象,可以像其它对象那样操作和传递。
不同的调用方式:
直接调用:foo();
对象方法:o.method();
构造器:new Foo();
原型链上的方法: call/apply/bind func.call(o)
函数声明与表达式
最常见的两种创建函数的方式是函数声明和函数表达式。
函数声明: function add(a,b){ return a+b; } 函数表达式: var add = function(a,b){ return a+b; }; //立即执行函数表达式 (function(){ })(); //函数对象作为返回值 return function() { } //命名式函数表达式 var add = function foo (a,b){ }
区别:
函数声明会被前置,可在声明前调用。
函数表达式仅将函数变量的声明前置。
函数表达式允许匿名,函数声明不可以。
函数声明不可立即调用,函数表达式可以立即调用。this
全局的this,浏览器中指window
console.log(this === window); //true this.a = 38; console.log(window.a); //38
作为对象方法的函数的this
var o = { prop:38, f:function() { return this.prop; } } console.log(o.f()); //38
对象原型链上的this
var o ={f:function(){return this.a + this.b}}; var p =Object.create(o);//空对象,原型指向o p.a = 1; p.b =4; console.log(p.f()); //5
构造器中的this
function MyClass(){ this.a = 37; } var o = new MyClass(); console.log(o.a); //37 function C2(){ this.a = 37; return {a:38}; } o = new C2(); console.log(o.a); //38
上面例子中,用new调用,this指向空对象,对象指向原型为MyClass().prototype的空对象,无return语句,会将this作为返回值。而C2()中,返回的是一个对象,o不再是this,就是{a:38}。
call/apply方法与this
function add(c,d){ return this.a + this.b + c + d; } var o = {a:1,b:3}; add.call(o,5,7); //1+3+5+7 = 16 add.apply(o,[10,20]); 34 function bar(){ console.log(Object.prototype.toString.call(this)); } //达到调用一些没法直接调用的方法的目的 bar.call(7); //"[object Number]"
bind方法(ie 9+)
function f (){ return this.a; } var g = f.bind({a:"test"}); //将某个对象作为this,更改了作用域,this.a 返回的是test console.log(g()); //test var o = {a:37,f:f,g:g}; console.log(o.f(),o.g()); //37 ,test
函数方法
arguments - 类数组对象,与传入参数相关 function foo(x,y,z){ arguments.length; //2 arguments[0]; arguments[0] = 10; x; // change to 10 arguments[2] = 100; //未传参数,失去绑定关系 z; //undefined!!! } foo(1,2); foo.length; //3 foo.name; //'foo'
apply/call 方法
function foo(x,y){ console.log(x,y,this); } foo.call(100,1,2); //1,2,Number(100) foo.apply(true,[3,4]); //3,4,Boolean(true)
bind方法 (ie9 +)
改变作用域this.x = 9; var module = { x:81, getX:function(){ return this.x; } } module.getX(); //81 var getX = module.getX; getx(); //9 var boundGetX = getx.bind(module); boundGetX(); //81
函数科里化(把一个函数拆成多个单元)
function add(a,b,c){ return a+b+c; } var func = add.bind(undefined,100); //103,提供额外参数,将100绑定在a上 var func2 = func.bind(undefined,200); func2(10); //310 //实际应用场景 function getConfig(colors,size,otherOptions){ console.log(colors,size,otherOptions); } var defaultConfig = getConfig.bind(null,'#fff',"1024*768"); defaultConfig('123');//这样每次使用时只要传入会变化的参数即可
new与bind
fundtion foo() { this.b = 100; return this.a; } var func = foo.bind({a:1}); func(); //1 new func(); // {b:100} 用new时,bind会被忽略到。用new时,return的除非是对象,不然会把this作为返回值,而且this会被初始化为一个空对象,这个对象的原型是foo.prototype .
闭包及作用域
一、闭包
闭包的例子
function outer() { var localVal = 30; return locakVal; } outer(); //30 function outer() { var localVal = 30; return function(){ return localVal; } } var func = outer(); func(); //30
闭包无处不在
!function() { var a = '222'; document.addEventListener('click',function(){ console.log(a); }); }(); jquery $.ajax({})
闭包误区-循环闭包
document.body.innerHtml = "<divid=div1>aaa</div><div id=div2>bbb</div><div id=div3>ccc</div>"; for (var i =1;i<4;i++){ document.getElementById('div'+i). addEventListener('click',function(){ alert(i); // all are 4 }); } //正确做法 document.body.innerHtml = "<divid=div1>aaa</div><div id=div2>bbb</div><div id=div3>ccc</div>"; for (var i =1;i<4;i++){ !function(i){ document.getElementById('div'+i). addEventListener('click',function(){ alert(i); // 1,2,3 }); }(i); //立即执行匿名函数 }
闭包-封装
(function(){ var _userId = 11; // 私有化变量 var export = {}; function converter(userId){ return +userId; } export.getUserId = function(){ return converter(_userId); } window.export = export; }()); export.getUserId(); //11 只能通过对象提供的方法获取函数内部的私有化变量 export._userId; //undefined
小结
优点:灵活和方便;封装
缺点:空间浪费,内存泄露,性能消耗
二、作用域&执行上下文(待完善)
全局作用域;js无块级作用域;函数作用域,变量声明(if else while for里的变量声明写在里面和外面没有区别)
最外层函数封装,将全局变量转化为函数变量处理:
(function(){
//do sth
var a,b;
})();
!function(){
//do sth
var a,b;
}();
执行上下文:
重复调用时,每次调用函数都有不同的上下文。
变量对象(VO)
JS OOP
1 . OOP
面向对象程序设计是一种程序设计范型,同时也是一种程序开发的方法。对象指的是类的实例。它将对象作为程序的基本单元,将程序和数据封装其中,以提高软件的重用性、灵活性和扩展性。
特点:继承、封装、多态、抽象
2 . 基于原型的继承(实例)
function Foo(){
this.y = 2;
}
typeif Foo.prototype; // "object"
Foo.prototype.x = 1;
var obj = new Foo();
obj.y //2
obj.x //1
prototype是函数对象上预设的对象属性,而原型是对象上的原型,通常是其构造器的prototype属性
Foo.prototype
{
constructor:Foo,
_proto_:Object.prototype,
x:1
}
实例
function Person(name,age){
this.name = name;
this.age = age;
}
Person.prorotype.hi = function() {
console.log('my name is ' + this.name + ",I'm " + this.age + 'years old');
}
person.prototype.LEGS_NUM = 2;
person.prototype.ARMS_NUM = 2;
Person.prototype.walk = function() {
console.log(this.name + 'is walking');
}
function Student(name,age,className){
Person.call(this,name,age);
this.className = className;
}
Student.prototype = Object.create(Person.prorotype);//创建一个空对象,其原型指向Person.prorotype !!!!
Student.prototype.constructor = student;
Student.prototype.hi = function(){
console.log('my name is ' + this.name + ",I'm " + this.age + 'years old and from'+ this.className );
}
student.prototype.learn = function(subject){
console.log(this.name + 'is learning' + subject + 'at' + this.className);
}
//test
var boss = new Student('boss',27,'3.2');
boss.LEGS_NUM; //2
boss.walk();//boss is walking
3 . 原型链(实例分析)
上面例子中,boss实例的原型指向构造器的prototype属性。
构造器的prototype - student.prototype由Object.create(Person.prototype)构造,实际上它是一个空对象,
且原型指向Person.prototype
Person.prototype设置了很多属性方法,它本身也有原型,它指向Object.prototype(因此随便一个对象都有toString,valueof,hasOwnProterty等方法...)
最终,boss实例调用hi方法时,首先看对象本身有无hi方法。没有的话向上查找,在student.prototype上找到并调用。所有属性及方法通过原型链一层一层往上找。
特例,并非所有对象都有Object.prototype原型
var obj = Object.create(null); //创建空对象,原型指向参数
Object.getPrototypeOf(obj); //undefined
function abc(){}
var binded = abc.bind(null);
binded.prototype //undefined
4 . prototype属性
改变prototype
Student.prototype.x = 101;
boss.x; //101
修改属性时会影响,但修改整个原型时,并不会更改已经实例的对象
Student.prototype = {y:2};
boss.y //undefined
boss.x //101
可以使用definePrototy方法设置对象原型属性,可借此模拟解决一些版本支持问题
Object.defineProperty(Object.prototype,'x',{writable:true,value:1});
new/原型链
funtion foo(){}
foo.prototype.z = 3; //foo.prototype -> Object.prototype -> null
var obj = new foo();
obj.y = 2;
obj.x = 1;
5 . 实现继承的方式
function Person() {}
function Student() {}
Student.prototype = Object.create(Person.prototype);//创建一个空对象,可以调用原型链的属性和方法,同时不影响原型链上的东西。原型链的写不向上继承