如题: html
function A() {
B = function () {console.log(10)}
return this
};
A.B = function () {console.log(20)};
A.prototype.B = function () {console.log(30)}
var B = function () {console.log(40)}
function B() {console.log(50)}
A.B() // answer 20 【原型与原型链】
// 在`A`的原型对象中查找是否有`B`函数而且调用,这里并未执行`A`函数。
// A.B = function () {console.log(20)};
// 在A的原型对象中添加了`B`函数,中止查找,因此答案为 20
B() // answer 40 【函数表达式和函数声明】
// var B = function () {console.log(40)}
// function B() {console.log(50)}
// 同名 -> 函数提高会 > 变量提高
// 换言之 同名的函数表达式和函数声明同时存在时 老是执行表达式
A().B() // answer 10 【函数表达式和函数声明】
// A() 执行函数A ==> 1.变量B从新赋值函数 2.返回this(window)
// .B() 执行全局下的B函数 已经被从新赋值 因此输出10
B() // answer 10
// 上面的代码执行过A函数了,此时全局下的B函数输出10
new A.B() // answer 20【函数表达式和函数声明】
// new 执行了 A.B = function () {console.log(20)};
new A().B() // answer 30
// new 执行构造函数 A ,全局变量 B 从新赋值函数10
// 此时A() 指针指向哪里?
// 首先要知道 new 作了什么事?
// ==> 建立空对象objA objA.__proto__ = A.prototype
// .B() 在A的原型对象中查找 B; A.prototype 指向函数的原型对象
// A.prototype.B = function () {console.log(30)} 输出 30
复制代码
每个函数都有一个
prototype
属性。git
function Foo() {}
Foo.prototype; // {constructor,__proto__}
复制代码
不管何时,只要建立了一个新函数,根据一组特定的规则为该函数建立一个prototype 属性,这个属性指向函数的原型对象。github
那么这个建立的原型对象是什么呢?express
{
constructor: ƒ Foo(),
__proto__: Object
}
复制代码
每个原型对象都有一个
constructor
属性函数
建立了自定义的构造函数后,其原型对象只会默认取得 constructor
属性。这个属性解决了对象识别问题,便可以经过该属性判断出实例是由哪一个构造函数建立的。post
Foo.prototype.constructor === Foo; // true
复制代码
前面说了,原型对象只会默认取得 constructor
属性,那么原型对象的其余属性(好比:__proto__
)是这么来的呢,这就要说到 __proto__
指针了。ui
每个实例都有一个
__proto__
指针,指向构造函数的原型对象。this
var foo = new Foo();
foo.__proto__ === Foo.prototype; //true
复制代码
上面提到的构造函数的原型对象它自己也是一个实例,因此在它内部会有一个__proto__
指针。spa
ECMAScript
中提供了构造函数来建立新对象。但构造函数自己就是一个函数,与普通函数没有任何区别,只不过为了区分,通常将其首字母大写,但这并非必须的。prototype
函数被 new 关键字调用时就是构造函数。
function f(name) {
console.log("execute");
this.name = name;
}
var k = new f("k"); // execute =====> 调用new
console.log(k); // {name: "k"}
var h = f("h"); // execute =====> 未调用new
console.log(h); // undefined
复制代码
从上面代码能够看出:
new
,函数的角色就成为了构造函数,建立一个对象并返回。var obj = {}; // 建立一个空对象
obj.__proto__ = constructor.prototype;//添加__proto__属性,并指向构造函数的prototype 属性。
constructor.call(this); // 绑定this
return obj;
复制代码
new 关键字的内部实现机制:
原型链的理论主要基于上述提到的构造函数、实例和原型的关系:
constructor
属性__proto__
指针 其中最最重要的是第三条,依赖这条关系,层层递进,就造成了实例与原型的链条。接着上面的探索,构造函数的原型的原型是由 Object
生成的,那么 Object
的原型是由什么生成?而原型链的终点又是在哪?
Object.prototype.__proto__ // null
null.__proto__; // Uncaught TypeError: Cannot read property '__proto__' of null
// game over
复制代码
原型的终点是 null
,由于 null
没有 proto
属性。
最后以一个例子来理解上面所谈到的原型与原型链
function Foo(){} // 构造函数 Foo
var foo = new Foo() // foo.__proto__ 指向 Foo.prototype
复制代码
函数声明是用指定的参数声明一个函数。一个被函数声明建立的函数是一个 Function
对象,具备 Function
对象的全部属性、方法和行为。
// 函数声明语法
function name([param[, param[, ... param]]]) { statements }
复制代码
在函数表达式中咱们能够忽略函数名称建立匿名函数,并将该匿名函数赋值给变量。
var add = function(a, b) {
return a + b;
};
add(2, 3) // 5
复制代码
固然, 也能够建立命名函数表达式 Named function expression:
var add = function func(a, b) {
return a + b;
};
add(2, 3) // 5
复制代码
命名函数表达式中函数名称只能做为函数体做用域内的局部变量,外部不可访问。
var a = function pp(v) {
v++;
if (v>3) {
return v;
} else {
return pp(v);
}
}
a(1); // 4
pp(1); // ReferenceError: pp is not defined
复制代码
对于函数声明建立的函数,能够在本做用域内任意位置访问。
a(); // 1
function a() {
return 1;
}
a(); // 1
复制代码
而函数表达式不会。
console.log(a); // undefined (只是变量提高)
a(1); // TypeError: a is not a function
var a = function(v) {
console.log(v);
};
复制代码
console.log(fn); // [Function: fn]
var fn = function () {
console.log(1);
}
function fn() {
console.log(2);
}
fn() // 1
复制代码
提高过程
// 函数提高
function fn() {
console.log(2);
}
// 变量提高
var fn;
fn = function () {
console.log(1);
}
fn() //最终输出1
复制代码