javascript快速入门12—函数式与面向对象

函数

函数是一组可以随时随地运行的语句。

函数是 ECMAScript 的核心。

创建函数

  1. function fnOne(){//具有名称的函数,函数名必须符合变量名命名规范
  2. //可以没有符何语句
  3. }
  4. var fnTwo =function(){//匿名函数
  5. };
  6. function(){//创建匿名函数而不立即创建其引用,那么之后就没办法调用此函数
  7. }
  8. (function fnThree(){
  9. })();//创建函数并立即执行一次
  10. (function(){})();//创建匿名函数并立即执行一次

匿名函数与命名函数的区别

  1. fnOne();//不会出错,使用function创建的具有名称的函数在任何与之相同作用域的地方都能调用
  2. fnTwo();//出错
  3. var fnTwo =function(){};//因为只有执行了这个赋值语句后,fnTwo才会被创建
  4. function fnOne(){}

函数返回值

  1. function fnTest(){return"value";//使用return来返回值
  2. alert("Hello!!!");//执行了return之后,函数就会退出
  3. }

函数参数

  1. function fnTest(arg1,arg2,arg3){
  2. alert(arg1+"\n"+arg2+"\n"+arg3);
  3. }
  4. fnTest(1,2,3);
  5. fnTest(1,2);//没有传值过去时,就会是undefined

arguments对象:在函数执行时函数内部就会有arguments对象,它包含了所有的参数,arguments的length属性报告了传入参数个数

  1. function fnTest(){for(var i=0;i< arguments.length;i++){
  2. alert(arguments[i]);
  3. }
  4. }
  5. fnTest(1,2,3);
  6. fnTest(45);

使用arguments对象模拟函数重载

  1. function fnTest(){var args = arguments;switch(arguments.length){case0:return"没有输入!!!";case1:return"一个参数:"+args[0];case2:return"二个参数:"+args[0]+" 和 "+ args[1];
  2. }
  3. }
  4. alert(fnTest());
  5. alert(fnTest(1));
  6. alert(fnTest(2));

arguments对象补充:arguments对象的callee属性指向它所在的函数

  1. function fnTest(){alert(arguments.callee);}

闭包

闭包,指的是词法表示包括不被计算的变量的函数,也就是说,函数可以使用函数之外定义的变量。

在 ECMAScript 中使用全局变量是一个简单的闭包实例。请思考下面这段代码:

  1. var msg ="我是全局变量!!!";function say(){
  2. alert(msg);
  3. }
  4. say();

在ECMAScript中,在函数声明处向函数外部看到的声明的所有变量,在函数内部都能访问到它们的最终值!

  1. var g ="全局变量!!!";function fnA(){var a="A";function fnB(){var b="B";
  2. alert(a);//可以访问到a
  3. alert(c);//但不以访问c
  4. function fnC(){var c ="C";
  5. alert(a+"\n"+b);//只要遵循从里向外看的原则,看到的变量都可以访问到
  6. }
  7. }
  8. }//更复杂的闭包
  9. function fnTest(num1,num2){var num3 = num1+num2;returnfunction(){
  10. alert("num1+num2结果为"+num3);
  11. };
  12. }var result = fnTest(23,56);
  13. result();

闭包函数只能访问变量的最终值!!!

  1. function fnTest(arr){for(var i=0;i < arr.length;i++){
  2. arr[i]=function(){
  3. alert(i+" | "+arr[i]);
  4. };
  5. }
  6. }var arr =[0,1,2,3,4,5];
  7. fnTest(arr);for(var i=0;i < arr.length;i++){
  8. arr[i]();//始终输出6还有一个undefined
  9. //因为函数退出后,i值为6,所以访问到的值只有6
  10. }

不但在闭包中可以访问闭包外的变量值,而且还可以设置它的值

  1. function fnTest(){var a=123;return{
  2. set:function(param){a = param},
  3. get:function(){return a}
  4. };
  5. }var obj = fnTest();
  6. alert(obj.get());//123
  7. obj.set(4);
  8. alert(obj.get());//4

对象,构造函数

创建一个对象

  1. var obj =newObject();
  2. alert(obj);
  3. alert(Object);//一个函数
  4. Object();//可以直接执行
  5. //构造函数也是一个普通的函数
  6. functionDemo(){
  7. }var d =newDemo();//不会出错,使用new运算符来创建对象实例
  8. alert(d);//object

this关键字的用法

  1. functionDemo(){this.property="属性!!!";
  2. }
  3. d =newDemo();
  4. alert(d.property);//属性!!!

不使用new而直接执行构造函数时,this指向window

  1.    functionDemo(){this.property="属性!!!";
  2. }var d =Demo();
  3. alert(d.property);//undefined
  4. alert(window.property);//属性!!!

可以给构造函数传递参数,然后可以将参数赋值给对象的属性

  1. functionPerson(name,age){this.name = name;this.age = age;
  2. }var p1 =newPerson("CJ",18);

instanceof 运算符,用来判断对象是否是某个类(虽然ECMAScript中并不存在类,但我们在这里依然使用这一术语)的实例

  1. var str =newString("string");
  2. alert(str instanceofString);//true
  3. var arr =newArray();
  4. alert(arr instanceofArray);//true
  5. functionDemo(){}var d =newDemo();
  6. alert(d instanceofDemo);//true

面向对象术语

一种面向对象语言需要向开发者提供四种基本能力:

  • 封装——把相关的信息(无论数据或方法)存储在对象中的能力。
  • 聚集——把一个对象存储在另一个对象内的能力。
  • 继承——由另一个类(或多个类)得来类的属性和方法的能力。
  • 多态——编写能以多种方法运行的函数或方法的能力。
    ECMAScript支持这些要求,因此可被看作面向对象的。

封装与私有属性:封装并不要求私有!

  1. functionPerson(name,age){this.name = name;//将值存储为对象的属性即是封装
  2. this.age = age;
  3. }var p1 =newPerson("CJ",18);

ECMAScript目前版本并不支持私有属性,但可以通过闭包来模拟

  1. functionPerson(name,age){this.getName =function(){return name};this.setName =function(param){name=param};this.getAge =function(){return age};this.setAge =function(param){age=param};
  2. }var p1 =newPerson("CJ",18);
  3. alert(p1.name);//undefined
  4. alert(p1.getName());//CJ
  5. p1.setName("XXX");
  6. alert(p1.getName());//XXX

继承:prototype属性

ECMAScript中,继承是通过构造函数的prototype属性实现的

  1. functionPerson(name,age){this.name=name;this.age = name;
  2. }
  3. alert(Person.prototype);//object
  4. Person.prototype.sort ="人";var p1 =newPerson("CJ",18);
  5. alert(p1.sort);//所有的Person对象的实例继承了sort这个属性

所有对象都有一个方法isPrototypeOf(),用来判断它是不是另一个对象的原型

  1. functionPerson(){}var p1 =newPerson();
  2. alert(Person.prototype.isPrototypeOf(p1));//true

在ECMAScript中让一个类继承另一个类的方式比较特殊

  1. functionClassA(){this.a ="A";
  2. }functionClassB(){this.b ="B";
  3. }
  4. ClassB.prototype =newClassA();//让ClassB继承ClassA
  5. var b =newClassB();
  6. alert(b.a);//"A",继承了属性a
  7. alert(b instanceofClassB);//true
  8. alert(b instanceofClassA);//true,因为继承,b也是ClassA的后代
  9. alert(ClassB.prototype.isPrototypeOf(b));//true
  10. alert(ClassA.prototype.isPrototypeOf(b));//true,ClassA.prototype也是b的原型

然而这样的继承有个注意点——

  1. functionClassA(){this.a ="A";
  2. }functionClassB(){this.b ="B";
  3. }var b =newClassB();//先实例化ClassB
  4. ClassB.prototype =newClassA();//再去继承ClassA,将prototype重置为另一个对象
  5. alert(b instanceofClassB);//false
  6. alert(b instanceofClassA);//false
  7. alert(ClassB.prototype.isPrototypeOf(b));//false
  8. alert(ClassA.prototype.isprototypeOf(b));//false

当构造函数需要参数时

  1. functionPerson(name,age){this.name = name;this.age = age;
  2. }functionTeacher(name,age,lesson){this.tempMethod =Person;//对象冒充
  3. this.tempMethod(name,age);//当执行Person时,由于是在Teacher某个实例上调用的,所以在Person函数中的this指向了Teacher的实例
  4. deletethis.tempMethod;//删除临时方法
  5. this.lesson = lesson;
  6. }
  7. ClassB.prototype =newClassA();//始终不应在继承时放参数
  8. var t1 =newTeacher("HUXP",18,"C#");
  9. alert(t1.name+" | "+this.age+" | "+this.lesson);

事实上,对于对象冒充,ECMAScript提供了更简洁的内置方法call,在每个函数上调用,第一个参数即为要冒充的对象,剩下的是函数需要的其它参数

  1. functionDemo(arg){this.name = arg;
  2. }var obj =newObject();
  3. Demo.call(obj,"name");
  4. alert(obj.name);//"name"
  5. //使用call重写上面继承的例子
  6. functionPerson(name,age){this.name = name;this.age = age;
  7. }functionTeacher(name,age,lesson){
  8. Person.call(this,name,age);//对象冒充
  9. this.lesson = lesson;
  10. }

静态属性与Function类

在ECMAScript里有个有趣的地方是,函数本身也是对象(和数组也一样),也可使用new来创建.Function构造函数至少要传两个字符串参数,可以是空字符串。除了最后一个字符串会被当做函数体语句去执行,其它参数都会作为函数参数变量名!

  1. var fn =newFunction();//一个空函数
  2. //创建有内容的函数
  3. fn =newFunction("arg1","alert(arg1)");//最后一个参数为执行语句的字符串,前面参数全是函数要用到的参数
  4. //上面的代码等效于
  5. fn =function(arg1){alert(arg1)};//同样,由于都是赋值语句,所以要注意出现次序
  6. fn.property="既然是对象,那么就要以添加属性"

事实上,在构造函数上添加的属性就被称之为静态属性,它不会被实例所继承

  1. functionClassDemo(){
  2. }
  3. ClassDemo.property=newArray();var d =newClassDemo();
  4. alert(d.property);//undefined
  5. alert(ClassDemo.property);//object

Function类的实例还有其它一些方法和属性(当然,使用function关键字声明的函数也是一样的)

  1. function outer(){
  2. inner();function inner(){
  3. alert(inner.caller);//函数的caller属性指向调用自身的上下文函数,这里指向outer
  4. }
  5. }function applyTest(){var args = arguments;this.name = args[0];this.age=args[1];
  6. }var obj =newObject();
  7. applyTest.apply(obj,["name",18]);
  8. alert(obj.name);
  9. alert(obj.age);//apply方法与call方法类似,也用于对象冒充,只是apply方法只有两个参数
  10. //第一个是要冒充的对象,第二个是所有参数组成的数组

原文: https://wizardforcel.gitbooks.io/liyanhui-tutorials/content/85.html