// 一个函数是直接调用的,this指向window。
//哪个对象调用了函数,函数里面的this就指向谁
// 实际上this的最终指向的是那个调用它的对象
//但是在箭头函数里面this指向的是定义时的对象而不是调用时的对象
// 如果一个函数中有this,
// 但是它没有被上一级的对象所调用,
// 那么this指向的就是window,
// 严格版中的默认的this不再是window,而是undefined
// this表示当前对象,如果在全局作用范围内使用this,则指代当前页面对象window;
// 如果在函数中使用this,则this指代什么是根据运行时此函数在什么对象上被调用。
// 我们还可以使用apply和call两个全局方法来改变函数中this的具体指向。
// prototype本质上还是一个JavaScript对象。 并且每个函数都有一个默认的prototype属性。
function 模拟实现call() {
将函数设为对象的属性
执行该函数
删除该函数
var foo = {
value: 1
};
function bar() {
console.log(this.value);
}
// 第一步
foo.fn = bar
// 第二步
foo.fn()
// 第三步
delete foo.fn
// 对于后面的参数:
// 我们可以从 Arguments 对象中取值,取出第二个到最后一个参数,然后放到一个数组里。
// 以上个例子为例,此时的arguments为:
// arguments = {
// 0: foo,
// 1: 'kevin',
// 2: 18,
// length: 3
// }
// 因为arguments是类数组对象,所以可以用for循环
var args = [];
for(var i = 1, len = arguments.length; i < len; i++) {
args.push('arguments[' + i + ']');
}
// 执行后 args为 [foo, 'kevin', 18]
// 不定长的参数问题解决了,我们接着要把这个参数数组放到要执行的函数的参数里面去。
// 我们这次用 eval 方法拼成一个函数,类似于这样:
eval('context.fn(' + args +')')
// 这里 args 会自动调用 Array.toString() 这个方法。
Function.prototype.call2 = function(context) {
context.fn = this;
var args = [];
for(var i = 1, len = arguments.length; i < len; i++) {
args.push('arguments[' + i + ']');
}
eval('context.fn(' + args +')');
delete context.fn;
}
// this 参数可以传 null,当为 null 的时候,视为指向 window
Function.prototype.call2 = function (context) {
var context = context || window;
context.fn = this;
var args = [];
for(var i = 1, len = arguments.length; i < len; i++) {
args.push('arguments[' + i + ']');
}
var result = eval('context.fn(' + args +')');
delete context.fn
return result;
}
}
function 模拟实现apply() {
Function.prototype.apply = function (context, arr) {
var context = Object(context) || window;
context.fn = this;
var result;
if (!arr) {
result = context.fn();
}
else {
var args = [];
for (var i = 0, len = arr.length; i < len; i++) {
args.push('arr[' + i + ']');
}
result = eval('context.fn(' + args + ')')
}
delete context.fn
return result;
}
}
function 模拟实现bind() {
// bind() 方法会创建一个新函数。当这个新函数被调用时,bind() 的第一个参数将作为它运行时的 this,之后的一序列参数将会在传递的实参前传入作为它的参数
Function.prototype.bind2 = function (context) {
if (typeof this !== "function") {
throw new Error("Function.prototype.bind - what is trying to be bound is not callable");
}
var self = this;
var args = Array.prototype.slice.call(arguments, 1);
var fNOP = function () {};
var fBound = function () {
var bindArgs = Array.prototype.slice.call(arguments);
self.apply(this instanceof fNOP ? this : context, args.concat(bindArgs));
}
fNOP.prototype = this.prototype;
fBound.prototype = new fNOP();
return fBound;
}
}
----------------------------------
1.
function a(){
var user = "追梦子";
console.log(this.user); //undefined
console.log(this); //Window
}
a();
2.
var o = {
user:"追梦子",
fn:function(){
console.log(this.user); //追梦子
}
}
o.fn();
3.
var o = {
user:"追梦子",
fn:function(){
console.log(this.user); //追梦子
}
}
window.o.fn();
// 这个函数中包含多个对象,尽管这个函数是被最外层的对象所调用,this指向的也只是它上一级的对象
4.
var o = {
a:10,
b:{
a:12,
fn:function(){
console.log(this.a); //undefined
console.log(this); //window
}
}
}
var j = o.b.fn;
j();
// this永远指向的是最后调用它的对象,也就是看它执行的时候是谁调用的,
// 例子4中虽然函数fn是被对象b所引用,但是在将fn赋值给变量j的时候并没有执行所以最终指向的是window,
// 这和例子3是不一样的,例子3是直接执行了fn。
5.
function fn(){
this.user = '追梦子';
return {};
}
var a = new fn;
console.log(a.user); //undefined
function fn(){
this.user = '追梦子';
return undefined;
}
var a = new fn;
console.log(a.user); //追梦子
// 如果返回值是一个对象,那么this指向的就是那个返回的对象,如果返回值不是一个对象那么this还是指向函数的实例。
// 还有一点就是虽然null也是对象,但是在这里this还是指向那个函数的实例,因为null比较特殊。
function fn(){
this.user = '追梦子';
return null;
}
var a = new fn;
console.log(a.user); //追梦子
----------------------------------
function b() {
return function () {
return this;
}
}
console.log(b()()); //This:指向windows
function c() {
return {
s: function () {
return this;
}
}
}
console.log(c().s()); //This:指向s