자주 쓰는 말:
function add(a,b) { return a + b; } alert(add(1,2)); // 结果 3
이와 같은 함수를 정의하면 함수 내용이 컴파일됩니다. 가서 전화해) 게다가 이 함수가 생성되면 같은 이름의 객체도 생성된다는 사실을 모르실 수도 있습니다. 예를 들어 이제 "add"라는 개체가 있습니다(자세한 내용은 아래 함수: 개체 섹션 참조).
익명 함수:
A를 할당할 수도 있습니다. 이를 정의하기 위해 변수 이름이 익명 함수에 제공됩니다.
var add = function(a,b) { return a + b; } alert(add(1,2)); // 结果 3
이 코드는 이전 예제와 동일한 작업을 수행합니다. 구문이 이상해 보일 수도 있지만 함수가 개체이고 방금 개체에 이름을 할당했다는 사실을 더 잘 이해할 수 있습니다. var myVar=[1,2,3]과 같은 문장이라고 생각하시면 됩니다. 이렇게 선언한 함수 내용도 컴파일됩니다.
이러한 기능을 할당할 때 반드시 익명 기능일 필요는 없습니다. 여기서는 위와 동일한 작업을 수행하지만 함수 이름 "theAdd"를 추가하고 함수 이름이나 해당 변수를 호출하여 함수를 참조할 수 있습니다.
var add = function theAdd(a,b) { return a + b; } alert(add(1,2)); // 结果 3 alert(theAdd(1,2)); // 结果也是 3
이러한 함수 정의 방법을 사용하면 객체지향 프로그래밍에 매우 유용합니다. 아래 속성과 같이 함수를 객체로 만들 수 있기 때문입니다.
var myObject = new Object(); myObject.add = function(a,b){return a+b}; // myObject 现在有一个叫做“add”的属性(或方法)” // 而且我能够象下面这样使用它 myObject.add(1, 2);
함수: 객체
함수는 자바스크립트에서 특수한 형태의 객체입니다. 첫 번째 [b] 클래스 데이터 유형입니다. 이는 속성을 추가할 수 있음을 의미합니다.
객체 생성
방금 언급한 것처럼 함수를 정의할 때 JavaScript는 실제로 뒤에서 객체를 생성합니다. 이 객체의 이름은 함수 이름 자체입니다. 이 객체의 유형은 function입니다. 다음 예에서는 깨닫지 못할 수도 있지만 실제로는 Ball이라는 객체를 생성했습니다.
function ball() // 也许看起来有点奇怪,但是这个声明 { // 创建了一个叫做Ball的对象 i = 1; } alert(typeof ball); // 结果 "function"
이 객체의 내용을 인쇄할 수도 있으며 함수의 실제 코드를 출력합니다
alert(ball); //结果为: //function ball() //{ // i = 1; //}
속성 추가
객체 함수를 포함하여 객체에 속성을 추가할 수 있습니다. 함수 정의의 본질은 객체를 생성하는 것이기 때문입니다. 우리는 "비밀스럽게" 함수에 속성을 추가할 수 있습니다. 예를 들어 여기서는 Ball 함수를 정의하고 callsign 속성을 추가합니다.
function Ball() // 也许看起来有点奇怪,但是这个声明创建了一个叫做Ball的对象,而且你能够引用它或者象下面那样给它增加属性 { } ball.callsign="The ball"; // 给Ball增加属性 alert(ball.callsign); // 输出 "The ball"
포인터
함수는 객체이므로 포인터를 함수에 할당할 수 있습니다. 다음 예에서와 같이 ptr 변수는 myFunction 개체를 가리킵니다.
function myFunction(message) { alert(message); } var ptr=myFunction; // ptr指向了myFunction ptr("hello"); // 这句会执行myFunction:输出"hello"
마치 함수 이름이 포인터 이름으로 대체된 것처럼 이 함수를 실행할 수 있습니다. 따라서 위에서 이 줄의 의미는 ptr("hello"); 와 myFunction("hello"); 입니다.
함수에 대한 포인터는 객체 지향 프로그래밍에서 매우 유용합니다. 예: 동일한 함수를 가리키는 여러 개체가 있는 경우(다음과 같이):
function sayName(name) { alert(name); } var object1=new Object(); // 创建三个对象 var object2=new Object(); var object3=new Object(); object1.sayMyName=sayName; // 将这个函数指派给所有对象 object2.sayMyName=sayName; object3.sayMyName=sayName; object1.sayMyName("object1"); // 输出 "object1" object2.sayMyName("object2"); // 输出 "object2" object3.sayMyName("object3"); // 输出 "object3"
포인터만 저장되므로(함수 자체는 저장되지 않음) ) , 함수 객체 자체를 변경하면 해당 함수에 대한 모든 포인터가 변경됩니다. 아래에서 볼 수 있습니다:
function myFunction() { alert(myFunction.message); } myFunction.message="old"; var ptr1=myFunction; // ptr1 指向 myFunction var ptr2=myFunction; // ptr2 也指向 myFunction ptr1(); // 输出 "old" ptr2(); // 输出 "old" myFunction.message="new"; ptr1(); // 输出 "new" ptr2();
을 가리키는 포인터 함수가 생성된 후 함수를 재할당할 수 있지만 포인터가 필요합니다. 함수 개체 자체에 대한 포인터가 아니라 함수 개체 자체에 대한 포인터입니다. 다음 예에서는 myfunction()의 내용을 변경하겠습니다.
function myFunction() { alert("Old"); } myFunction(); // 输出 "Old" myFunction=function() { alert("New"); }; myFunction(); // 输出 "New"
이전 기능은 어디에 있나요? ? 버려진.
유지해야 하는 경우 변경하기 전에 포인터를 할당할 수 있습니다.
function myFunction() { alert("Old"); } var savedFuncion=myFunction; myFunction=function() { alert("New"); }; myFunction(); // 输出 "New" savedFuncion(); // 输出 "Old"
인라인 기능
function get(a,b,c) { function cal(n) { return n/2; } var result = “”; result+=cal(a)+” ”; result+=cal(b)+” ”; result+=cal(c); } var resultString = get(10,20,30); alert(resultString); // 输出 "5 10 15"
내부에서만 사용할 수 있습니다. 중첩된 함수를 호출합니다. 즉,
getHalfOf.calculate(10)을 호출할 수 없습니다. 왜냐하면 Calculate는 외부 함수(getHalfOf())가 실행 중일 때만 존재하기 때문입니다. 이는 이전 논의와 일치합니다(함수는 컴파일되지만 호출할 때만 실행됩니다).
어떤 함수를 호출하나요?
이름 충돌을 고려하고 계실 수도 있습니다. 예를 들어, 다음 중 계산이라는 함수 중 어떤 함수가 호출됩니까?
function calculate(number) { return number/3; } function getHalfOf(num1, num2, num3) { function calculate(number) { return number/2; } var result=""; result+=calculate(num1)+" "; result+=calculate(num2)+" "; result+=calculate(num3); } var resultString=getHalfOf(10,20,30); alert(resultString); // 输出 "5 10 15"
이 예에서 컴파일러는 먼저 로컬 메모리 주소를 검색하므로 내장 계산 기능을 사용합니다. 인라인(로컬) 계산 함수를 삭제하면 이 코드는 전역 계산 함수를 사용합니다.
함수: 데이터 유형 및 생성자
다른 객체 유형과 매우 다른 함수의 또 다른 특별한 기능을 살펴보겠습니다. 함수는 데이터 유형에 대한 청사진으로 사용될 수 있습니다. 이 기능은 사용자 정의 데이터 유형을 시뮬레이션하기 위해 객체 지향 프로그래밍에서 자주 사용됩니다. 사용자 정의 데이터 유형을 사용하여 생성된 개체를 일반적으로 사용자 정의 개체라고 합니다.
데이터 유형
在定义了一个函数之后,我们也同时创建了一个新的数据类型。这个数据类型能够用来创建一个新对象。下例,我创建了一个叫做Ball的新数据类型。
function Ball() { } var ball0=new Ball(); // ball0 现在指向一个新对象 alert(ball0); // 输出 "Object",因为 ball0 现在是一个对象
这样看来,ball0=new Ball()作了什么?new关键字创建了一个类型是Object的新对象(叫做ball0)。然后它会执行Ball(),并将这个引用传给ball0(用于调用对象)。下面,你会看到这条消息:“creating new Ball”,如果Ball()实际上被运行的话。
function Ball(message) { alert(message); } var ball0=new Ball("creating new Ball"); // 创建对象并输出消息 ball0.name="ball-0"; // ball0现在有一个属性:name alert(ball0.name); // 输出 "ball-0"
我们可以把上面这段代码的第6行看做是底下的代码6-8行的一个简写:
function Ball(message) { alert(message); } var ball0=new Object(); ball0.construct=Ball; ball0.construct("creating new ball"); // 执行 ball0.Ball ("creating.."); ball0.name="ball-0"; alert(ball0.name);
这行代码ball0.construct=Ball和以上中的ptr=myFunction语法一致。
添加属性
当我们象上面那样使用关键字new创建一个对象的时候,一个新的Object被创建了。我们可以在创建之后给这个对象添加属性(就好像我在上面那样添加属性name。而接下来的问题就是如果我们创建了这个对象的另外一个实例,我们得象下面那样再次给这个新对象添加这个属性。)
function Ball() { } var ball0=new Ball(); // ball0 现在指向了类型Ball的一个新实例 ball0.name="ball-0"; // ball0 现在有一个属性"name" var ball1=new Ball(); ball1.name="ball-1"; var ball2=new Ball(); alert(ball0.name); // 输出 "ball-0" alert(ball1.name); // 输出 "ball-1" alert(ball2.name); // 哦,我忘记给ball2添加“name”了!
我忘记给ball2添加属性name了,如果在正式的程序中这也许会引发问题。有什么好办法可以自动增加属性呢?嗯,有一个:使用this关键字。this这个词在function中有特别的意义。它指向了调用函数的那个对象。让我们看看下面的另一个示例,这时候我们在构造函数中添加上这些属性:
function Ball(message, specifiedName) { alert(message); this.name=specifiedName; } var ball0=new Ball("creating new Ball", "Soccer Ball"); alert(ball0.name); // prints "Soccer Ball"
请记住:是new关键字最终使得构造函数被执行。在这个例子中,它将会运行Ball("creating new Ball", "Soccer Ball");而关键字this将指向ball0。
因此,这行:this.name=specifiedName变成了ball0.name="Soccer Ball"。它主要是说:给ball0添加属性name,属性值是Soccer Ball。
我们现在只是添加了一个name属性给ball0,看起来和上一个例子中所做的很象,但却是一个更好更具扩展性的方法。现在,我们可以随心所欲的创建许多带有属性的ball而无需我们手动添加它们。而且,人们也希望创建的Ball对象能够清晰的看懂它的构造函数并且能够轻松找出Ball的所有属性。让我们添加更多属性到Ball里。
function Ball(color, specifiedName, owner, weight) { this.name=specifiedName; this.color=color; this.owner=owner; this.weight=weigth; } var ball0=new Ball("black/white", "Soccer Ball", "John", 20); var ball1=new Ball("gray", "Bowling Ball", "John", 30); var ball2=new Ball("yellow", "Golf Ball", "John", 55); var balloon=new Ball("red", "Balloon", "Pete", 10); alert(ball0.name); // 输出 "Soccer Ball" alert(balloon.name); // 输出 "Balloon" alert(ball2.weight); // 输出 "55"
嘿!使用面向对象术语,你能够说Ball是一个拥有如下属性的对象类型:name,color, owner, weight。
将对象赋给属性我们并没被限制只能添加形如字符串或者数字之类的简单数据类型作为属性。我们也能够将对象赋给属性。下面,supervisor是Employee的一个属性.
function Employee(name, salary, mySupervisor) { this.name=name; this.salary=salary; this.supervisor=mySupervisor; } var boss=new Employee("John", 200); var manager=new Employee("Joan", 50, boss); var teamLeader=new Employee("Rose", 50, boss); alert(manager.supervisor.name+" is the supervisor of "+manager.name); alert(manager.name+"\'s supervisor is "+manager.supervisor.name);
函数也是一个对象。所以你可以让一个函数作为一个对象的一个属性。下面,我将添加两个函数getSalary和addSalary。
function Employee(name, salary) { this.name=name; this.salary=salary; this.addSalary=addSalaryFunction; this.getSalary=function() { return this.salary; }; } function addSalaryFunction(addition) { this.salary=this.salary+addition; } var boss=new Employee("John", 200000); boss.addSalary(10000); // boss 长了 10K 工资……为什么老板工资可以长这么多:'( alert(boss.getSalary()); // 输出 210K……为什么默认工资也那么高……:'( addSalary和getSalary演示了几种将函数赋给属性的不同方法。 如前面数次提到的,一个函数声明的结果是一个对象 被创建。 function Employee(name, salary) { this.name=name; this.salary=salary; this.addSalary=addSalaryFunction; this.getSalary=function() { return this.salary; }; } function addSalaryFunction(addition) { this.salary=this.salary+addition; } var boss=new Employee("John", 200000); var boss2=new Employee("Joan", 200000); var boss3=new Employee("Kim", 200000);
当你创建这个对象的更多实例时(boss2和boss3),每一个实例都有一份getSalary代码的单独拷贝;而与此相反,addSalary则指向了同一个地方(即addSalaryFunction)。
看看下面的代码来理解一下上面所描述的内容。
function Employee(name, salary) { this.name=name; this.salary=salary; this.addSalary=addSalaryFunction; this.getSalary=function() { return this.salary; }; } function addSalaryFunction(addition) { this.salary=this.salary+addition; } var boss1=new Employee("John", 200000); var boss2=new Employee("Joan", 200000); // 给getSalary函数对象添加属性 boss1.getSalary.owner="boss1"; boss2.getSalary.owner="boss2"; alert(boss1.getSalary.owner); // 输出 "boss1" alert(boss2.getSalary.owner); // 输出 "boss2" // 如果两个对象指向同一个函数对象,那么 上面两个输出都应该是“boss2”。 // 给addSalary函数对象添加属性 boss1.addSalary.owner="boss1"; boss1.addSalary.owner="boss2"; alert(boss1.addSalary.owner); // 输出 "boss2" alert(boss2.addSalary.owner); // 输出 "boss2" // 因为两个对象都指向同一个函数 // 当修改其中一个的时候,会影响所有的实例(所以两个都输出“boss2”).
也许不是重要的事情,但这里有一些关于运行类似上面的getSalary的内嵌函数的结论:
1) 需要更多的存储空间来存储对象(因为每一个对象实例都会有它自己的getSalary代码拷贝);
2) javascript需要更多时间来构造这个对象。
让我们重新写这个示例来让它更有效率些。
function Employee(name, salary) { this.name=name; this.salary=salary; this.addSalary=addSalaryFunction; this.getSalary=getSalaryFunction; } function getSalaryFunction() { return this.salary; } function addSalaryFunction(addition) { this.salary=this.salary+addition; }
看这儿,两个函数都指向同一个地方,这将会节约空间和缩短构造时间(特别是当你有一大堆内嵌函数在一个构造函数的时候)。这里有另外一个函数的功能能够来提升这个设计,它叫做prototype,而我们将在下一节讨论它。
函数:原型
每一个构造函数都有一个属性叫做原型(prototype,下面都不再翻译,使用其原文)。这个属性非常有用:为一个特定类声明通用的变量或者函数。
prototype的定义
你不需要显式地声明一个prototype属性,因为在每一个构造函数中都有它的存在。你可以看看下面的例子:
function Test() { } alert(Test.prototype); // 输出 "Object"
给prototype添加属性
就如你在上面所看到的,prototype是一个对象,因此,你能够给它添加属性。你添加给prototype的属性将会成为使用这个构造函数创建的对象的通用属性。
例如,我下面有一个数据类型Fish,我想让所有的鱼都有这些属性:
livesIn="water"和price=20;为了实现这个,我可以给构造函数Fish的prototype添加那些属性。
function Fish(name, color) { this.name=name; this.color=color; } Fish.prototype.livesIn="water"; Fish.prototype.price=20;
接下来让我们作几条鱼:
var fish1=new Fish("mackarel", "gray"); var fish2=new Fish("goldfish", "orange"); var fish3=new Fish("salmon", "white");
再来看看鱼都有哪些属性:
for (int i=1; i<=3; i++) { var fish=eval("fish"+i); // 我只是取得指向这条鱼的指针 alert(fish.name+","+fish.color+","+fish.livesIn+","+fish.price); }
输出应该是:
"mackarel, gray, water, 20" "goldfish, orange, water, 20" "salmon, white water, 20"
你看到所有的鱼都有属性livesIn和price,我们甚至都没有为每一条不同的鱼特别声明这些属性。这时因为当一个对象被创建时,这个构造函数将会把它的属性prototype赋给新对象的内部属性__proto__。这个__proto__被这个对象用来查找它的属性。
你也可以通过prototype来给所有对象添加共用的函数。这有一个好处:你不需要每次在构造一个对象的时候创建并初始化这个函数。
用prototype给对象添加函数
function Employee(name, salary) { this.name=name; this.salary=salary; } Employee.prototype.getSalary=function getSalaryFunction() { return this.salary; } Employee.prototype.addSalary=function addSalaryFunction(addition) { this.salary=this.salary+addition; }
我们可以象通常那样创建对象:
var boss1=new Employee("Joan", 200000); var boss2=new Employee("Kim", 100000); var boss3=new Employee("Sam", 150000);
并验证它:
alert(boss1.getSalary()); // 输出 200000 alert(boss2.getSalary()); // 输出 100000 alert(boss3.getSalary()); // 输出 150000
这里有一个图示来说明prototype是如何工作的。这个对象的每一个实例(boss1, boss2, boss3)都有一个内部属性叫做__proto__,这个属性指向了它的构造器 (Employee)的属性prototype。当你执行getSalary或者addSalary的时候,这个对象会在它的__proto__找到并执行这个代码。