原型
什么是原型?什么是构造函数、实例原型、实例?它们的关系是什么?
原型(Prototype)
每个 JavaScript 对象都有一个内部链接到另一个对象的引用,这个对象被称为原型。当试图访问一个对象的属性时,JavaScript 会首先在这个对象自身上寻找该属性,如果找不到,则会查找该对象的原型,如此递归下去,直到找到该属性或达到原型链的末端。
构造函数(Constructor)
构造函数是用于创建对象的函数。通过 new 操作符调用构造函数时,它会创建一个新对象,并将这个新对象的内部 [[Prototype]] 连接到构造函数的 prototype 属性。
1 2 3 4 5
| function Person(name) { this.name = name; }
const person = new Person('Yuyang');
|
Person是构造函数,person是创建的对象
实例原型(Instance Prototype)
实例原型是由构造函数的 prototype
属性引用的对象。所有由该构造函数创建的实例对象都将共享这个实例原型对象的属性和方法。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| function Person(name) { this.name = name; }
Person.prototype.callName = function(){ console.log("zyz"); }
const person1 = new Person("Yuyang"); const person2 = new Person("YuyangA"); person1.callName = function(){ console.log(this.name) } person1.callName(); person2.callName();
|
实例(Instance)
实例是通过构造函数创建的具体对象。每个实例都有一个内部链接到它的构造函数的 prototype
属性的引用,这个链接可以通过 __proto__
(非标准)或 Object.getPrototypeOf
方法来访问。
1 2
| const person3 = new Person('Charlie'); console.log(person3.__proto__ === Person.prototype);
|
原型链
词法作用域
JavaScript 采用词法作用域(lexical scoping),也就是静态作用域。
this
JavaScript中的this总是指向一个对象,而具体指向哪个对象是在运行时基于函数的执行环境动态绑定的,而非函数被声明时的环境。
this的指向
- 作为对象的方法调用
- 作为普通函数调用
- 构造器调用
- Function.prototype.call 或 Function.prototype.apply 调用
作为对象的方法调用:
函数作为对象的方法被调用时,this指向该对象:
1 2 3 4 5 6 7 8
| var obj = { a: 1, getA: function () { alert(this === obj); alert(this.a); } } obj.getA();
|
作为普通函数调用:
当函数不作为对象的属性被调用时,此时的this总是指向全局对象。在浏览器的javascript里,这个全局对象是window对象。
1 2 3 4 5 6 7 8
| window.name = "globalName";
var getName = function(){ return this.name; }
console.log(getName());
|
1 2 3 4 5 6 7 8 9 10 11 12
| window.name = "globalName";
var myObject = { name: "sven", getName: function(){ return this.name; } }
var getName = myObject.getName; console.log(getName());
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41
| window.id = "window"; document.getElementById("div1").addEventListener("click", function() { console.log("🚀 ~ window.id:", this.id, this) var callback = function(){ console.log("🚀 ~ window.id:", this.id, this) } callback() })
document.getElementById("div1").addEventListener("click", function() { console.log("🚀 ~ window.id:", this.id, this); var callback = () => { console.log("🚀 ~ window.id:", this.id, this); }; callback(); });
document.getElementById("div1").addEventListener("click", function() { console.log("🚀 ~ window.id:", this.id, this); var callback = function(){ console.log("🚀 ~ window.id:", this.id, this); }.bind(this); callback(); });
document.getElementById("div1").addEventListener("click", function() { console.log("🚀 ~ window.id:", this.id, this); var self = this; var callback = function(){ console.log("🚀 ~ window.id:", self.id, self); }; callback(); });
|
构造器调用:
用new调用函数时,该函数会返回一个对象,通常情况下,构造器的this就会指向返回的这个对象,
1 2 3 4 5 6
| var MyClass = function(){ this.name = "seven"; }
var obj = new MyClass(); alert(obj.name);
|
当构造器显式的返回一个对象时,那么此次运算结果最终会返回这个对象,而不是我们之前期待的this
1 2 3 4 5 6 7 8
| var MyClass = function(){ this.name = "sven"; return { name: "anne" } } var obj = new MyClass(); alert(obj.name);
|
Function.prototype.call 或 Function.prototype.apply 调用:
1 2 3 4 5 6 7 8 9 10 11 12 13
| var obj1 = { name: 'seven', getName: function(){ return this.name; } }
var obj2 = { name: 'anne' }
console.log(obj1.getName()) console.log(obj1.getName.call(obj2))
|
call
方法调用一个函数,并显式地指定 this
值和传递的参数。
call源码实现:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
| Function.prototype.myCall = function(context, ...args) { context = context || globalThis; const fnSymbol = Symbol(); context[fnSymbol] = this; const result = context[fnSymbol](...args); delete context[fnSymbol]; return result; };
function greet(greeting, punctuation) { console.log(greeting + ', ' + this.name + punctuation); }
const person = { name: 'Alice' }; greet.myCall(person, 'Hello', '!');
|
call() 方法接受的是参数列表,而 apply() 方法接受的是一个参数数组。
call:
function.call(thisArg, arg1, arg2, …)
apply:
func.apply(thisArg, [argsArray])
bind
方法创建一个新的函数,该函数在调用时,其 this
值和传递的参数被预先设置。与 call
方法不同,bind
不会立即调用函数,而是返回一个新的函数。
1 2 3 4 5 6 7 8 9 10 11 12
| var foo = { value: 1 }
Function.prototype.bind2 = function(context){ var _this = this; return function(){ _this.apply(context); } }
var bindFoo2 = bar.bind2(foo);
|
丢失的this