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

Js基础回顾 - 函数作用域及js oop(1)

2016-03-21 09:06 651 查看

函数和作用域

什么是函数?

函数是一块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(
4000
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);//创建一个空对象,可以调用原型链的属性和方法,同时不影响原型链上的东西。原型链的写不向上继承
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: