自己开网站工作室,大型网站改版,wordpress 调用文章列表,fixed wordpressthis指向 普通函数#xff1a;this 的指向由调用方式决定#xff0c;可以是全局对象、调用该函数的对象#xff0c;或者显式指定的对象。箭头函数#xff1a;this 的指向在定义时确定#xff0c;始终继承自外层函数作用域的 this#xff0c;不会被调用方式影响。 var obj… this指向 普通函数this 的指向由调用方式决定可以是全局对象、调用该函数的对象或者显式指定的对象。箭头函数this 的指向在定义时确定始终继承自外层函数作用域的 this不会被调用方式影响。 var obj {print: function() {var test () {console.log(yupi, this);}test();},rap: {doRap:() {console.log(this);}}
}
var copyPrint obj.print;
copyPrint();
obj.print();
obj.rap.doRap();//直接在 obj.rap 对象中定义并没有包裹在任何外层函数中会继承全局作用域的 thisvar obj {name: yupi,func: function() {var self this;console.log(this.name);console.log(self.name);(function() {console.log(this.name);console.log(self.name);}());}
};
obj.func();
//输出
yupi
yupi
undefined
yupivar num 2;
function print() {console.log(this.num);
}var obj {num: 4,test1: print,test2: function () {print();},
};
obj.test1();
obj.test2();
var foo obj.test1;
foo();
//输出结果
4
2
2function test(num){this.value num;return this;
}
var value test(5);
var obj test(6);console.log(value.value);
console.log(obj.value);
//输出结果
**undefined**6
function test(value) {this.num value;
}var obj1 {test: test,
};
obj1.test(1);
console.log(obj1.num);var obj2 {};
obj1.test.call(obj2, 2);
console.log(obj2.num);var foo new obj1.test(3);
console.log(obj1.num);
console.log(foo.num);//输出结果
1213function test(value){this.num value;
}var obj1 {};
var testBind test.bind(obj1);
testBind(1);
console.log(obj1.num);var foo new testBind(2);
console.log(obj1.num);
console.log(foo.num);
//输出结果
1
1
2 优先级new 绑定显示绑定( apply/call/bind ) 隐式绑定( obj.foo())默认绑定( 独立函数调用 ) 立即执行函数表达式 IIFE
什么是 IIFE立即执行函数表达式
IIFEImmediately Invoked Function Expression是一种 JavaScript 编程模式它使一个函数在定义时立即执行。IIFE 的格式通常如下所示 (function() { // 函数体 })(); (() { // 函数体 })(); 立即执行函数的执行机制
在上面的代码中IIFE 是通过 (function() {...})() 形式定义的。它是一个 函数表达式并且在定义后立即执行。也就是说IIFE 在 初始化时 执行一次并且其作用域内的代码也只会执行一次。
对应到您的代码中的 IIFE
在您提供的代码中IIFE 部分是
window.num 2;
var obj {num: 4,test: (function(){console.log(this);this.num * 6;return function(){console.log(this);this.num * 8;}})()
}
var test obj.test;
test();
obj.test();
console.log(obj.num);
console.log(window.num);//输出结果
Window 对象
Window 对象
{num: 4, test: ƒ} // obj 对象
32
96这段代码中的 IIFE 会在 定义 test 属性时立即执行。console.log(this) 在 IIFE 执行时打印的是 全局对象 window然后 this.num * 6; 会修改 window.num 的值。该 IIFE 返回了一个匿名函数这个匿名函数会赋值给 obj.test。
总结IIFE 是立即执行的因此它的代码块只会执行一次并且返回的函数会被赋给 obj.test。这个返回的函数之后会被调用但 IIFE 本身只在定义时执行一次。
解释执行顺序 在执行到 var obj { ... } 这一行时IIFE 被立即执行 console.log(this) 打印 window 对象。this.num * 6 修改了 window.num2 * 6 12。然后IIFE 返回一个匿名函数该匿名函数被赋给 obj.test。 之后obj.test 就是 IIFE 返回的匿名函数该函数在后面的调用中会执行 第一次调用 test() 时this 指向 windowwindow.num 被修改为 96。第二次调用 obj.test() 时this 指向 objobj.num 被修改为 32。
IIFE 的核心特性
只执行一次IIFE 在定义时立即执行并且它的作用域只会被创建一次。返回值IIFE 可以返回任何值在您的代码中它返回了一个函数这个函数被赋值给 obj.test。默认this指向windows(非严格模式
⭐⭐进阶题
var length 10;
function func() {console.log(this.length);
}var obj {length: 5,test: function(func) {console.log(this.length)//5func();//相当于window.func()被window对象调用this指向window对象//func.call(this)//这样修改this指向this就是指向objarguments[0]();//类似于func()但是调用方式不同this不同含义大不相同这个可以看作函数被arguments对象调用而arguments对象本省就有length属性即为参数的个数所以this.length输出为2参数个数}
};
obj.test(func, 1);输出为 102 刚看到一道JS面试题关于this指向的问题 var length 10; function func() { console.log(this.length); } var obj { length: 5, test: function(func) { console.log(this.length)//5 func();//相当于window.func()被window对象调用this指向window对象 //func.call(this)//修改this指向this就是指向obj arguments[0]();//类似于func()但是调用方式不同this不同含义大不相同这个可以看作函数被arguments对象调用而arguments对象本身就有length属性即为参数的个数所以this.length输出为2参数个数 } }; obj.test(func, 1); ⭐扩展知识词法作用域
在 JavaScript 中**词法作用域Lexical Scope**是指函数的作用域即变量的可访问范围是根据函数定义的位置来确定的而不是根据函数调用的位置来决定的。这意味着 JavaScript 中的作用域链是静态的它依赖于代码在编写时的结构。
1. 什么是词法作用域
词法作用域也叫静态作用域是指一个函数在定义时决定了它能访问哪些变量。即函数访问外部变量的规则与它们的定义位置有关而与函数如何被调用时的调用位置无关。
- **函数的作用域链**是由函数定义时外部的作用域链决定的而不是调用时的执行环境。 2. 如何理解词法作用域
- 在 JavaScript 中**变量的作用域**决定了变量的可访问范围。 - **函数的作用域**决定了函数内部可以访问哪些变量。
当你定义一个函数时它会记住外部的作用域链并且无论何时调用它函数总是能访问到它定义时可访问的那些变量。
举个简单的例子
function outer() {var outerVar I am from outer!;function inner() {console.log(outerVar); // 访问外部函数的变量}inner(); // 调用 inner 函数
}outer(); // 输出 I am from outer!在这个例子中 - inner 函数定义在 outer 函数内部因此它可以访问 outer 函数中定义的变量 outerVar。 - 这就是**词法作用域**因为 inner 函数的作用域是由它定义的位置即在 outer 函数内部决定的而不是由它调用的位置决定的。 3. 词法作用域与 this
在 JavaScript 中this 的指向和作用域是两个不同的概念。虽然 this 和作用域链都与上下文相关但它们的行为方式不同。理解词法作用域有助于理解 **箭头函数** 中 this 的行为。
普通函数和箭头函数的区别
- **普通函数**中的 this 是根据**调用时的上下文**来确定的。 - **箭头函数**中的 this 是在**定义时继承**外层作用域的 this这就是箭头函数不同于普通函数的一大特点。
4. 箭头函数中的 this
箭头函数中的 this 绑定规则与普通函数不同。普通函数的 this 是动态绑定的而箭头函数的 this 是静态的指向定义时外层的 this。
例子
function Outer() {this.name Outer;// 普通函数this.printName function() {console.log(this.name); // this 指向调用它的对象};// 箭头函数this.arrowPrintName () {console.log(this.name); // this 会继承自 Outer 函数的 this};
}var obj new Outer();
obj.printName(); // 输出 Outerthis 指向 obj
obj.arrowPrintName(); // 输出 Outerthis 仍然指向 Outer 函数的 this
在上面的例子中 - printName 是普通函数它的 this 会根据调用时的上下文来确定。 - arrowPrintName 是箭头函数它的 this 会继承外层 Outer 函数中的 this即指向创建它时的 this而不受 obj 的影响。
5. 作用域链
每个 JavaScript 函数都会创建一个作用域链用于查找变量。当一个函数内部访问变量时JavaScript 会沿着作用域链查找这个变量。如果函数内部没有定义某个变量JavaScript 会查找函数外部的作用域直到全局作用域为止。
- **作用域链**是由多个作用域组成的最里层的是当前函数的作用域外层是它的外部作用域一直到全局作用域。javascript
function outer() {var outerVar outer;function inner() {var innerVar inner;console.log(outerVar); // 在内层函数中访问外层函数的变量}inner();
}outer(); // 输出 outer
在上面的例子中 - inner 函数在 outer 函数内部定义它访问了外层的 outerVar。这是因为 inner 函数的作用域链包含了 outer 函数的作用域。
6. 词法作用域和闭包
https://gisjing.blog.csdn.net/article/details/143593869?spm1001.2014.3001.5502ydrefereraHR0cHM6Ly9ibG9nLmNzZG4ubmV0L20wXzU1MDQ5NjU1P3R5cGU9YmxvZw%3D%3D
闭包是指函数能够记住并访问它定义时的作用域即使这个函数在定义时的作用域已经执行完毕。
function outer() {var outerVar outer;function inner() {console.log(outerVar); // 访问 outer 的变量}return inner;
}var closure outer(); // 返回 inner 函数
closure(); // 输出 outer
在这个例子中inner 函数是一个闭包因为它记住了定义时的作用域即 outer 函数的作用域即使在外部调用时outerVar 仍然是可以访问的。
7. 词法作用域与变量提升
JavaScript 中的**变量提升**hoisting指的是在执行代码之前所有的 var 声明会被提升到作用域的顶部但它们的赋值并不会被提升。javascript
function example() {console.log(myVar); // undefined因为声明被提升了但赋值在后面var myVar 10;
}example();
在这个例子中myVar 的声明被提升了但是它的值 10 是在执行到那行代码时才赋值的。所以输出是 undefined而不是 10。
♥8. 总结 - **词法作用域**决定了变量和函数的可访问范围它是由函数的定义位置而不是调用位置来确定的。 - **作用域链**函数查找变量时会沿着作用域链从内到外查找直到全局作用域。 - **箭头函数的 this**箭头函数的 this 不是根据调用方式决定的而是继承自它定义时的外层作用域。 - **闭包**函数可以记住并访问它定义时的作用域即使外层函数已经执行完毕。 原型链
Javascript原型链-CSDN博客 boy.prototype-undefinednull.__proto__-TypeError
⭐⭐⭐JS中new关键字实例化对象具体做了什么⭐⭐⭐ 创建一个空对象 当new关键字被调用时JavaScript引擎首先会创建一个空白的对象。这个对象没有任何属性和方法但它会继承自构造函数的prototype属性所向的原型对指象。 设置原型链 接下来JavaScript引擎会将新创建的对象的内部原型__proto__设置为构造函数的prototype属性。这一步是原型链继承的关键它允许新对象访问构造函数原型中定义的方法和属性。 绑定this并执行构造函数 然后JavaScript引擎会将构造函数作为普通函数调用但此时this关键字的值会被设置为新创建的对象。这意味着在构造函数内部你可以通过this来引用新对象并向其添加属性和方法。构造函数中的代码会执行可能会初始化对象的属性、调用其他方法等。 返回新对象 最后如果构造函数没有显式地返回一个对象即返回的不是一个非原始值那么new表达式会默认返回新创建的对象。如果构造函数返回了一个对象那么new表达式会返回这个对象而不是新创建的那个空对象。需要注意的是这种情况下原型链的链接会失去作用因为返回的对象与构造函数的原型没有直接联系。 创建一个新的空对象并将这个对象的原型设置为函数的 prototype 属性。绑定函数内部的 this 到这个新对象即在函数内 this 指向新创建的对象。执行函数将函数内部的代码与新对象的作用域绑定。返回值处理如果函数返回一个非 null 的对象则 new 表达式会返回这个对象否则它会返回创建的对象。 实现继承的几种方式
1.原型链继承
子类prototype指向父类实例。缺所有实例共享父类属性修改子类实例会影响到所有实例
function Coder() {this.type Coder;
}Coder.prototype.rap function () {console.log(yo yo yo);
};function Yupi(name) {this.name name;this.age 18;
}// 原型链继承
Yupi.prototype new Coder();
Yupi.prototype.constructor Yupi;// 测试
const yupi new Yupi(Yupi);
console.log(yupi.type); // 输出: Coder
yupi.rap(); // 输出: yo yo yo2.构造函数
子类构造函数调用父类构造函数实现可继承父类属性但不能继承父类原型方法
function Coder() {this.type Coder;
}Coder.prototype.rap function () {console.log(yo yo yo);
};function Yupi(name) {Coder.call(this); // 调用父类构造函数this.name name;this.age 18;
}// 测试
const yupi new Yupi(Yupi);
console.log(yupi.type); // 输出: Coder
// yupi.rap(); // 这行代码会报错因为构造函数继承无法继承原型链上的方法3.组合继承组合1和2
开销较大两次调用父类构造函数
function Coder() {this.type Coder;
}Coder.prototype.rap function () {console.log(yo yo yo);
};function Yupi(name) {Coder.call(this); // 调用父类构造函数this.name name;this.age 18;
}// 组合继承
Yupi.prototype new Coder();
Yupi.prototype.constructor Yupi;// 测试
const yupi new Yupi(Yupi);
console.log(yupi.type); // 输出: Coder
yupi.rap(); // 输出: yo yo yo4.寄生组合推荐⭐
避免组合继承两次调用父类构造函数
function Coder() {this.type Coder;
}Coder.prototype.rap function () {console.log(yo yo yo);
};function Yupi(name) {Coder.call(this); // 调用父类构造函数this.name name;this.age 18;
}// 寄生组合继承
Yupi.prototype Object.create(Coder.prototype);
Yupi.prototype.constructor Yupi;// 测试
const yupi new Yupi(Yupi);
console.log(yupi.type); // 输出: Coder
yupi.rap(); // 输出: yo yo yo5.ES6 class语法
class Coder {constructor() {this.type Coder;}rap() {console.log(yo yo yo);}
}class Yupi extends Coder {constructor(name) {super(); // 调用父类构造函数this.name name;this.age 18;}
}// 测试
const yupi new Yupi(Yupi);
console.log(yupi.type); // 输出: Coder
yupi.rap(); // 输出: yo yo yo**基础补充**
function Father(){this.a11;this.b[1,2,this.a];this.printfunction(){console.log(this.a,this.b,typeof(this.b[2]));}
}
const fathernew Father();
father.print();
father.a13;
father.print();
//输出
//11 [1,2,11] number
//13 [1,2,11] number
//b中的this.a是复制a的值类型为原始类型
自己身上没有才去原型链上找
function Obj1() {}
function Obj2(value) {this.value value;
}
function Obj3(value) {if (value) {this.value value;}
}Obj1.prototype.value 1;
Obj2.prototype.value 1;
Obj3.prototype.value 1;console.log(new Obj1().value);
console.log(new Obj2().value);
console.log(new Obj3(666).value);1
undefined
666函数原型与对象原型
var FuncObj function () {};
Object.prototype.foo function () {console.log(foo);
};
Function.prototype.bar function () {console.log(bar);
};
FuncObj.foo();
FuncObj.bar();var f new FuncObj();
f.foo();
f.bar();//输出结果
foo
bar
foo
TypeError: f.bar is not a function⭐⭐**进阶题1**⭐⭐
前端分享一经典有难度易错的Javascript题目
function Father() {this.a 1;this.b [1, 2, this.a];this.c { field: 5 };this.print function () {console.log(this.a, this.b, this.c.field);};
}function Son() {this.a 2;this.update function () {this.b.push(this.a);this.a this.b.length;this.c.field this.a;};
}Son.prototype new Father();
var father new Father();
var son1 new Son();
var son2 new Son();
son1.a 11;
son2.a 12;
father.print();
son1.print();
son2.print();
son1.update();
son2.update();
father.print();
son1.print();
son2.print();1 [ 1, 2, 1 ] 5
11 [ 1, 2, 1 ] 5
12 [ 1, 2, 1 ] 5
1 [ 1, 2, 1 ] 5
5 [ 1, 2, 1, 11, 12 ] 5
6 [ 1, 2, 1, 11, 12 ] 5代码执行过程及结果分析 定义 Father 和 Son 构造函数 Father 构造函数初始化属性 a值为1b值为 [1, 2, this.a]即 [1, 2, 1]c对象 { field: 5 }以及 print 方法。Son 构造函数初始化属性 a值为2和 update 方法。Son.prototype new Father(); 使得 Son 的实例对象能够继承 Father 中定义的 a、b、c 和 print。 实例化对象 var father new Father(); 创建了一个 Father 的实例 father其属性值为 a 1b [1, 2, 1]c { field: 5 }。var son1 new Son(); 和 var son2 new Son(); 创建了两个 Son 的实例 son1 和 son2。由于继承了 Father它们各自拥有独立的 a 值由 Son 构造函数初始化为2和独立的 b、c 的引用通过继承 Father 的实例化对象。因此son1 和 son2 的初始属性为 a 2b [1, 2, 1]c { field: 5 }。 修改属性 son1.a 11; 将 son1 的 a 属性设置为11。son2.a 12; 将 son2 的 a 属性设置为12。 调用 print 方法 father.print(); 输出 father 的属性a 1b [1, 2, 1]c.field 5。结果为1 [1, 2, 1] 5son1.print(); 输出 son1 的属性a 11b [1, 2, 1]c.field 5。结果为11 [1, 2, 1] 5son2.print(); 输出 son2 的属性a 12b [1, 2, 1]c.field 5。结果为12 [1, 2, 1] 5 调用 update 方法 son1.update(); 执行以下操作 this.b.push(this.a); 将 son1.a即11添加到 b 中因此 son1.b 变为 [1, 2, 1, 11]。this.a this.b.length; 将 son1.a 更新为 b 的长度4。this.c.field this.a; 将 son1.c.field 设置为 a即4然后自增 a 为5。更新后son1.a 5son1.b [1, 2, 1, 11]son1.c { field: 4 }。 son2.update(); 执行以下操作 this.b.push(this.a); 将 son2.a即12添加到 b 中因此 son2.b 变为 [1, 2, 1, 11, 12]。this.a this.b.length; 将 son2.a 更新为 b 的长度5。this.c.field this.a; 将 son2.c.field 设置为 a即5然后自增 a 为6。更新后son2.a 6son2.b [1, 2, 1, 11, 12]son2.c { field: 5 }。 再次调用 print 方法 father.print(); 仍然输出原始的 father 属性a 1b [1, 2, 1]c.field 5。结果为1 [1, 2, 1] 5son1.print(); 输出更新后的 son1 属性a 5b [1, 2, 1, 11, 12]c.field 5。结果为5 [1, 2, 1, 11, 12] 5son2.print(); 输出更新后的 son2 属性a 6b [1, 2, 1, 11, 12]c.field 5。结果为6 [1, 2, 1, 11, 12] 5
在这个示例中son1 和 son2 实例共享了 Father 原型中的 b 和 c 引用因此 update 方法对 b 数组的修改会影响到所有 Son 实例。
⭐⭐**进阶题2**⭐⭐
function FuncObj() {print function () {console.log(1);};//重新给print变量赋值return this;//返回了this
}FuncObj.print function () {console.log(2);
};FuncObj.prototype.print function () {console.log(3);
};var print function () {console.log(4);
};function print() {console.log(5);
}FuncObj.print();
print();
FuncObj().print();//相当于调用windows.print()
print();
new FuncObj.print();//调用静态方法FuncObj.print()
new FuncObj().print();//构造方法FuncObj()实例化对象对象的print没找到-原型上找
new new FuncObj().print();//构造方法FuncObj()实例化对象找原型上print()再调用此方法-》仍输出3
//输出结果
2
4变量提升var与函数声明同名时var声明会在提升阶段覆盖函数声明
1
1
2
3
3
调用和输出分析
1. FuncObj.print();
这是调用 FuncObj 的静态方法 print。输出2
2. print();
此时 print 是定义为 var print function () { console.log(4); }。输出4
3. FuncObj().print();
调用 FuncObj()由于 FuncObj 返回 this即全局对象 window它会将 print 重新定义为 function () { console.log(1); }覆盖了全局的 print。然后调用 print()即 function () { console.log(1); }。输出1
4. print();
上一步中 print 被重新赋值为 function () { console.log(1); }。输出1
5. new FuncObj.print();
new FuncObj.print() 调用的是 FuncObj 的静态方法 print而非实例化 FuncObj。new 操作符在此上下文中无意义但会正常执行。【简单来讲就是不涉及原型链和this使用】输出2 为什么 new 在这里无意义 在这个过程中new 仅仅用来调用 FuncObj.print并没有创建 FuncObj 的实例。它的主要效果仅是创建一个与 FuncObj 无关的空对象作为 FuncObj.print 的 this并执行了 FuncObj.print 函数体。 new 在此无意义因为 FuncObj.print 并不使用 this返回的空对象也没有用。效果等同于 FuncObj.print()唯一的区别是返回一个空对象但对 FuncObj.print 的执行没有任何影响。 6. new FuncObj().print();
new FuncObj() 创建了一个 FuncObj 的实例该实例继承了 FuncObj.prototype.print 方法。调用实例的 print 方法即 FuncObj.prototype.print。输出3
7. new new FuncObj().print();
new FuncObj() 创建了一个 FuncObj 的实例且 new FuncObj().print 返回 FuncObj.prototype.print 方法。最外层 new 调用 FuncObj.prototype.print 方法仍然输出 3。输出3