函数的声明
javascript 中函数有两种声明方式:
// 声明方式
function doSometh() {
...
}
// 表达式方式
var doSometh = function () {
...
}
区别:声明方式定义的函数可以在函数调用之前也可以在函数调用之后,而表达式方式定义的函数只能在函数调用之前。
例如:
test();
var test = function () {
console.log('this is test method');
};
结果:
函数返回值
函数除显示 return 外,默认返回 undefined。
function test () {
}
console.log(test()); // 输出:undefined
立即执行函数
(function() { ... })();
((function() { ... })());
立即执行函数的作用:
- 保存上下文环境
- 作为命名空间
举个例子:
var data = [];
for(var i=0; i<3; i++) {
data[i] = function () {
console.log(i);
}
}
data[0]();
data[1]();
data[2]();
// 输出全部为3
我们修改一下代码再执行:
var data = [];
for(var i=0; i<3; i++) {
data[i] = (function (x) {
return function () {
console.log(x);
}
})(i);
}
data[0]();
data[1]();
data[2]();
// 输出0,1,2
构造函数
和普通函数不同,构造函数使用 new 关键字调用。
当构造函数没有形参时,可省略括号:
var o1 = new Object();
var o2 = new Object;
简单来说,构造函数做了这么几件事情:
- 创建一个空对象,作为将要返回的实例。
- 将空对象的
__proto__
指向构造函数的prototype
属性 - 将构造函数内部的 this指向创建的新对象
- 执行构造函数内部的代码
构造函数内部通常不使用 return,它返回构造出的新对象。
原型到原型链
每一个 js 对象都是一个属性集合。
每一个函数都有一个 prototype 属性。
举个例子,使用构造函数创建一个对象:
function Person() {
}
var p = new Person();
person.name = 'Tom';
console.log(person.name); // Tom
这个例子中,Person 就是一个构造函数,我们使用 new 关键字,创建了一个实例对象 person。
再看一个例子:
function Person() {
}
Person.prototype.name = 'Tom';
var person1 = new Person();
var person2 = new Person();
console.log(person1.name); // Tom
console.log(person2.name); // Tom
prototype
函数 Person 的 prototype 属性指向了一个对象,这个对象正是调用该构造函数而创建的实例的原型,也就是上面代码中的 person1 和 person2 的原型对象。
每一个JavaScript对象(null除外)在创建的时候就会与之关联另一个对象,这个对象就是我们所说的原型,每一个对象都会从原型"继承"属性。
constructor
默认情况下,所有原型对象都有 constructor(构造器)属性,这个属性指向所在的函数,即:Person.prototype.constructor 指向 Person
function Person() {
}
var person = new Person();
console.log(Person.prototype.constructor === Person); // true
用一张图表示构造函数和实例原型之间的关系:
在 ECMA 中,并没有规定直接访问对象原型的方法,但绝大部分浏览器都支持一个 __proto__
用来访问对象的原型。
function Person() {
}
var person = new Person();
console.log(person.__proto__ === Person.prototype); // true
更新下关系图:
综上得出:
function Person() {
}
var person = new Person();
console.log(person.__proto__ === Person.prototype); // true
console.log(Person.prototype.constructor === Person); // true
console.log(Object.getPrototypeOf(person) === Person.prototype); // true
当获取某个对象的某个属性时,首先从实例本身开始,如果找到了该属性,则返回该属性的值。如果没有找到,则搜索__proto__
指向的原型对象,如果找到,则返回值,如果没有则继续向上寻找,直到找到 Obejct。
Person 有自己的原型,即 Person.prototype
,同样 Person.prototype
也有自己的原型,即 Person.prototype.__proto__
属性:
那 Object.prototype 的原型呢?
我们可以尝试打印:
console.log(Object.prototype.__proto__ === null); // true
所以 Object.prototype.__proto__
的值为 null ,也就是说 Object.prototype
没有原型。
所以查找属性的时候查到 Object.prototype
就停止查找了。
最后一张关系图:
图中由相互关联的原型组成的链状结构就是原型链,也就是蓝色的这条线。