首页 > 代码库 > JavaScript-函数和OOP-学习笔记
JavaScript-函数和OOP-学习笔记
JavaScript权威指南学习笔记,禁止转载!
6、函数
函数调用方式:
直接调用:foo();
作为对象的方法调用:obj.method();
new:new Foo();
call/apply/bind:func.call(obj);
函数对象中没有return语句或者return的是基本类型时,如果有this返回值为this,
(1)创建函数
~函数声明:function foo(a,b){……}
~函数表达式:
1)var add= function(a,b){……};
2)立即执行函数表达式(function(){……})();、!function(){……} ();等
3)将函数作为返回值return function(){……};
4)命名式函数表达式var add= function foo(){……};
如,var func=function nfe(){};
alert(func===nfe); //ie6-8返回false,ie9报错 nfe is not defined
var func=function nfe(){/*……*/ nfe(); }; //递归调用。如果没有nfe还是可以用func来递归调用,所以不常用。
~Function构造器:var func=Function(‘a’,’b’,’console.log(a+b);’);//不常见
区别:函数声明会被前置,在定义该函数的作用域通过函数名调用;
函数表达式和函数构造器允许匿名,立即调用;
函数构造器没有函数名。
(2)this
全局作用域的this:指向window
一般函数的this:在浏览器里指向window,在node.js里指向global object
作为对象方法的函数的this:指向该对象
对象原型链上的this:对象原型链上的方法的函数的this指向该对象
对象get/set方法的函数的this:指向该对象
~~new与this:
function MyClass(){this.a=37; } //this指向一个空对象,该对象的原型是MyClass.prototype;
var o=new MyClass(); //this会作为MyClass函数的返回值赋给o;
o; //MyClass {a: 37}
o.a; //37
~~call/apply方法与this:
函数名. call(对象,arg1,arg2,argN)
函数名. apply(对象,[arg1,arg2,argN])
使函数里的this指向这个对象,并把之后的值赋值给函数。区别在于传参形式不一样。
如,function add(b,c){return this.a+b+c;} var o={a:1}; add.call(o,2,3);
//6
function add(b,c){return this.a+b+c;} var o={a:1}; add.apply(o,[4,5]);
//10
如,function foo(x,y){console.log(x,y,this);}
foo.call(100,1,2);
//1 2 Number {[[PrimitiveValue]]: 100} (如果第一个参数不是对象,会被转换为对象类型)
foo.call(null);
//undefined undefined Window
~~bind方法与this(ES5提供的方法,ie9+才支持)
函数名. bind(对象)使函数里的this指向这个对象。
~~bind与new与this
如function foo(){this.a=100;return this.b;}
var func=foo.bind({b:1});
func; //function foo(){this.a=100;return this.b;}
func(); //1
new func(); //foo {a: 100} (new调用函数时,this依旧指向一个空对象,该对象的原型是func.prototype,不会被bind所改变)
(3)arguments实参
foo.name:函数名,foo.length:函数形参的个数, arguments.length:实参的个数,
arguments.callee是一个指针,指向拥有这个arguments的函数。
~~当参数众多时,可使用bind与currying:
如,function add(a,b,c){return a+b+c;}
var fun=add.bind(undefined,100);
fun(1,2); //103(this指向无所谓,undefined/null都可以)
var fun2=fun.bind(undefined,200);
fun2(1); //301
?(未看) bind方法模拟
(4)闭包
问题:垃圾回收机制?性能优化:循环引用?
如,function outer(){var a=10; return function(){return a;} }
var func=outer();// function(){return a;} 作为返回值赋给了全局变量,所以a会一直在内存中,也可以在outer函数外部访问它内部的局部变量a了。
func(); //10
//javascript中函数内部可以读取全局变量,而函数外部无法读取函数内部的局部变量。
如何从函数外部读取函数内部的局部变量?在函数内部再定义一个函数。
优点:封装
缺点:父函数内的局部变量无法释放,空间浪费,内存消耗
把一个函数当做对象去传递作为返回值,类似有这样特性的语言都有闭包的概念。
(5)作用域
全局作用域
函数作用域
eval作用域,如eval(“var a=1;”)
ES6开始才有块级作用域
用途:利用函数作用域封装。
?(未看) ES3执行上下文
7、OOP(面向对象程序设计)
问题:原型链上的Object.prototype有哪些属性方法?
答:valueOf、toString、hasOwnProperty,
对象是类的实例,对象作为程序的基本单元,
特性:继承、封装、多态、抽象
(1)prototype
如,function Student(){……}
var bosn=new Student();
// Student.prototype是函数对象Student上预设的对象属性,它的作用是当使用new来构造Student的实例bosn时,Student.prototype会作为bosn的原型。
// Student.prototype.constructor指向Student本身(constructor:构造函数的意思)
//增加或删除Student.prototype上的属性会影响它所创建的实例bosn;
如果直接修改Student.prototype不会影响修改之前已经创建的实例bosn,
但会影响之后新创建的实例。
(2)instanceof
对象 instanceof 函数构造器:
判断函数构造器.prototype是否出现在对象的原型链上。右侧如果不是函数对象,会报错;左边如果不是对象而是基本类型时,会返回false,
如,new Object() instanceof Array;
//false 因为new Object()——>Object.prototype——>null,所以左侧对象的原型链上没有Array.prototype
(3)实现继承的方式
function Student(){……}
function Person(){……}
Student.prototype=new Person(); //不推荐
Student.prototype=Object.create(Person.prototype); //推荐(但ES5之后才支持)
(4)模拟重载
例,function Person(){
var args=arguments;//将实参赋给变量
if(typeof args[0]===’object’&& args[0]) {
//&& args[0] 是为了过滤null的参数(因为typeof args[0]也是object)
if(args[0]){
this.name= args[0].name; }
if(args[1]){
this.age= args[1].name; }
}
else{
if(args[0]){
this.name= args[0]; }
if(args[1]){
this.age= args[1]; }
}
}
var bosn=new Person(‘bosn’,27);
var bosn2=new Person( {name:‘bosn2’,age:27} );
(5)链式调用
如,function addclass(){}
addclass.prototype.add=function(str){
console.log(str+‘is added‘);
return this;
}//作为对象方法的函数的this指向该对象addclass.prototype
var myman=new addclass(); // myman的原型指向addclass.prototype
myman.add(‘A‘).add(‘B‘).add(‘C‘);
// Ais added
//Bis added
//Cis added
?(未看)9-1 抽象类
(6)例:探测器
子类,基类,detect(),analyze()当前的环境,实现两种探测器container和link
!function(global){
// DetectorBase基类
function DetectorBase(configs){
if(!this intanceof DetectorBase){
throw new Error(‘Do not invoke without new’); } //必须使用new调用DetectorBase,this指向该对象,该对象的原型指向DetectorBase.prptotype,所以this intanceof DetectorBase为true
this.configs= configs;
this.analyze();
}
DetectorBase.prototype.detect=function(){throw new Error(‘Not implemented’)};
DetectorBase.prototype.analyze=function(){ …… };
//具体类LinkDetector
function LinkDetector(links){
if(!this intanceof LinkDetector){
throw new Error(‘Do not invoke without new’); }//必须使用new调用
this.links=links;
DetectorBase.apply(this,arguments); //调用基类
}
// 具体类ContainerDetector
function ContainerDetector(containers){
if(!this intanceof ContainerDetector){
throw new Error(‘Do not invoke without new’); }//必须使用new调用
this.containers=containers;
DetectorBase.apply(this,arguments); //调用基类
}
//先继承
function inherit(subClass, superClass){
subClass.prototype =Object.create(superClass. prototype) ;
subClass.prototype.constructor= subClass;
}
inherit(LinkDetector, DetectorBase);
inherit(ContainerDetector, DetectorBase);
//再扩展,防止覆盖
LinkDetector. prototype. detect=function(){ …… };
ContainerDetector. prototype. detect=function(){ …… };
//冻结(使得extensible、configurable、writable:false,ES5方法)
Object.freeze(DetectorBase );
Object.freeze(DetectorBase. prototype );
Object.freeze(LinkDetector);
Object.freeze(LinkDetector. prototype );
Object.freeze(ContainerDetector);
Object.freeze(ContainerDetector. prototype );
//暴露到全局对象(在全局对象上添加了LinkDetector等三个属性,并且enumerable、configurable、writable:false)
Object.defineProperties(global,{
LinkDetector:{value: LinkDetector },
ContainerDetector:{value: ContainerDetector },
DetectorBase:{value: DetectorBase }
});
}(this); //立即执行函数至此结束,好长啊
原型链:LinkDetector——> LinkDetector. prototype(detect方法)——> DetectorBase. prototype(detect、analyze方法)
//调用
var cd=new ContainerDetector(‘abcdefg’);
var ld=new LinkDetector(‘http://www.imooc.com/’);
cd. detect();
ld. Detect();
//探测器终于结束了
JavaScript-函数和OOP-学习笔记