ECMAScript旮里旮旯儿之二(galigalaoer)

ECMAScript旮里旮旯儿之二(galigalaoer)
全局方法Global Function
1,函数的参数及参数对象Arguments
函数在定义时可以指定参数名称,也可以不指定
在调用函数时,可以传递参数,也可不传递,可以少传递,也可以多传递,没问题~
函数在调用时,除了能访问指定的参数外,还可以访问隐藏创建的参数对象:Arguments
arguments对象存储的是实际传递给函数的参数,而不局限于函数声明所定义的参数列表
网上盛传,传递多于定义个数个参数时,方法会忽略多出来的参数,实际上不但没有忽略还把它们安置在Arguments里了

2,无明确返回值的方法默认返回undefined
如下方法

function Person() { }
var p = Person();
alert(p)
//undefined

由于方法没有任何返回值,故p = undefined

3,参数和返回值可以是任何对象或方法,
如可以把某个方法当成参数传递(想像下C#中的Action,Func参数)
方法也可以作为返回值,想像下,参数是方法,返回值是方法,而参数和返回值的概念又是针对方法而言的,带劲啵?!
tips,自执行函数就是利用了返回值是方法从而可以直接把结果当成方法来调用,参考代码

(function (x) { 
alert(x)
//hello world
})("hello world");

静态方法Static Method

再加点材料进去,代码变成下边这样:

function Person() {}
Person.Hello
= function () {
alert(
"hello 张三")
}
Person.Hello();
//hello 张三

静态方法,OK,与C#中的静态方法一样一样的,直接调用就成
实例对象无法访问静态方法,下边的代码报错:

var p = new Person();
p.Hello();
//报错

在C#语言中,静态方法中是不允许使用this指针的,

而在ECMAScript语言中,静态方法可以使用this指针,并且指向自己,如下代码打印出"张三"

function Person() {}
Person.Name
= "张三";
Person.sayHello
= function () {
alert(
this.Name); //张三
alert(this instanceof Person)//false
alert(this instanceof Object)//true
}
Person.sayHello();

这里,Person实际上为object的一个实例

构造函数Constructor
现在把它当成Person类型的定义,使用new关键字来示例化它:

function Person() {
return "张三";
}
var p = new Person();
alert(
typeof p)//object
alert(p instanceof Person)//true

第一个打印object,说明这是个对象,再也不是"张三"字符串,而是张三对象

第二个打印结果表示这是个Person类型的变量!这是个人,这个人是张三

实例方法Instaqnce Method
很自然的,如何添加实例方法嘞?使用原型prototype

//也可以直接使用"this."把方法写在Person函数内部,
//
与原型方式的区别是构造Person实例时,Person函数内部的所有代码都要执行一遍,对于方法来说,很多时候是不必要的
//
function Person() {
//
this.name = "name in person";
//
this.setName = function (_name) {
//
this.name = _name;
//
}
//
}

//定义Person
function Person() {}
//实例属性
Person.prototype.Name = "张三";

原型上的成员被所有实例共享,所以所有实例的name都叫张三,这显示不符合自然规律,

应该让每个实例都拥有自己的name,很简单,只要为实例的属性赋值即可!赋值后该实例即得到一个属于自己的属性,
看下边的代码:

var p = new Person();
alert(p.Name);
//张三
var o = new Person();
o.Name
= "李四";
alert(o.Name);
//李四

p没有赋值操作,使用父类的值:张三

而o设置了姓名后拥有了自己的name属性,当这个独有的name属性被删除(delete)时,将重新访问原型中的name字段
稍微整理下啊,setter操作实例,产生实例自己的属性,getter访问沿着实例->实例类型的原型->实例类型的原型的原型->直至object.prototype为止

先聊聊原型对象prototype
这个prototype大有来头,打杯水,慢慢说;
在前边的实例方法时得到一点:实例在创建时将复制原型prototype上的所有成员:包括属性及属性值,方法及方法的实现
那这锅原型到底是个什么东东?
原型就是依附在类型上的一个实例,是个instance,是某类型的实例,不是某类型
比如,new Person()是个实例,而Person是类型,原型是前者
这个原型是创建实例的副本,克隆的概念,创建实例时,会把整个原型克隆一份送给实例
例如下边的代码定义了Person类型,给Person的原型添加name属性和sayHello方法,
这样在创建Person类型的实例时,p就自动复制了原型中的属性和方法:

function Person() {}
Person.prototype.name
= "张三";
Person.prototype.sayHello
= function () {
alert(
"hello in prototype");
}

var p = new Person();
p.sayHello();
//hello in prototype
alert(p.name);//张三

从上边的代码可以看出,方法和属性与原型完全一致,所谓的复制就是指这个了,

修改实例属性的值和方法,仅影响该实例,而不会影响原型中的方法和属性值,试一把,完整代码如下:

function Person() {}
Person.prototype.name
= "张三";
Person.prototype.sayHello
= function () {
alert(
"hello in prototype");
}

var p = new Person();
//重新实现实例方法
p.sayHello = function () {
alert(
"heelo in Person instance");
}
//重新实现实例属性
p.name = "改成李四了";

//仅影响当前实例的方法和属性
p.sayHello(); //heelo in Person instance
alert(p.name); //改成李四了


//不会影响原型的方法和属性
Person.prototype.sayHello(); //hello in prototype
alert(Person.prototype.name); //张三

//另一种访问原型的方法,实例->实例构造函数(这里就是Person了)->实例构造函数原型(就是Person.prototype了)
p.constructor.prototype.sayHello(); //hello in prototype
alert(p.constructor.prototype.name); //张三

再试试原型链继承prototype

先看代码:

//父类
function Person() { }
Person.prototype.name
= "name in Person";

//子类
function Student() { }
Student.prototype
= new Person();
Student.prototype.constructor
= Student;
Student.prototype.supr
= Person.prototype;

//创建子类实例
var stu = new Student();
Student.prototype.age
= 28;
Student.prototype.supr.name
= "name in Student instance";

//打印子类成员及父类成员
alert(stu.name); //name in Student instance
alert(stu.supr.name); //name in Person
alert(stu.age);//28

前两行定义Person类,随后定义Student类,
通过修改Student的原型为父类的实例,从而指定了实例化时要使用的副本,这里设置为Person类的实例,该实例只有一个name属性
而后修改子类原型的构造函数,重新指向到Student方法,
每个实例默认都拥有一个构造函数属性,这个构造函数默认指向当初创建自己的构造器
来看原型怎么创建的:
Student.prototype = new Person();
哦,新建了一个Person类,所以,Student.prototype.constructor = Person,这个原型的构造函数指向父类去了,
修复它其实很简单,将原型的构造函数重新指向子类自己即可:
Student.prototype.constructor = Student;

下边的代码为Student的实例提供了访问父类的通道,这样就可以访问父类中的成员了,如stu.supr.name
Student.prototype.supr = Person.prototype;

传送门(部分参考)
http://www.ijavascript.cn/jiaocheng/caller-callee-call-apply-464.html
http://joost.zeekat.nl/constructors-considered-mildly-confusing.html
http://www.cnblogs.com/windows7/archive/2010/03/22/1691316.html

原文地址:https://www.cnblogs.com/kkun/p/javascript_galigaoer_second.html