1、Js中的this
在 Js 中,this 是一个关键字,代表当前执行上下文中的对象。它的值取决于函数的调用方式,而不是定义时的位置。
1、默认绑定(对立函数调用)
当函数独立调用时 (非对象方法、非显示绑定),this 默认指向全局对象 (浏览器 中是window,Node 环境中是global )。严格模式下 ('use strict')this 为 undefined。
javascriptfunction func() { console.log(this) function func1() { console.log(this) } func1() // 浏览器环境输出 window 对象,Node环境输出 global 对象,严格模式下输出 undefined } func() // 浏览器环境输出 window 对象,Node环境输出 global 对象,严格模式下输出 undefined2、隐式绑定(放法调用)
当函数作为对象的方法 调用时,this 指向调用该方法的对象。
注意:若方法被赋值给变量后调用,会退回到默认绑定
javascriptconst obj = { name: "张三", fn: function() { console.log(this.name) } } obj.fn() // 输出 张三 此时的this 指向 obj const func = obj.fn; func() // 浏览器环境指向 window 对象,Node环境指向 global 对象,严格模式下指向 undefined3、显示绑定(call/apply/bind)
通过call、apply 或 bind 可以强制指定 this 的值。
apply :调用一个对象的一个方法,用另一个对象替换当前对象。例如:B.apply(A, arguments);即A对象调用B对象的方法。
call :调用一个对象的一个方法,用另一个对象替换当前对象。例如:B.call(A, args1,args2);即A对象调用B对象的方法。
bind :创建一个新的函数,在调用时,这个新函数的
this值会被绑定到bind()方法的第一个参数上,其余参数被预设到原函数的参数列表开头。
javaconst func() { console.log(this) } const obj = {}; func.apply(obj) // func.apply(thisArg, [argsArray]) func.call(obj) // func.apply(thisArg, arg1, arg2) const func1 = func.bind(obj) // func.bind 生成一个新的函数,将新函数的this指向obj4、new 绑定(构造函数绑定)
通过 new 调用构造函数时,this 指向新创建的实例对象
javascriptfunction Person(name) { this.name = name; console.log(this.name) } const zs = new Person("张三") console.log(zs.name); // 输出 张三 this 指向 zs5、箭头函数的 this
箭头函数 没有自己的 this,它会继承函数外层作用域的 this,并且无法通过显式绑定修改。
javascriptconst obj = { name: "张三", greet: () => { console.log(this.name) }, greet1: function () { console.log(this.name) } } obj.greet() // 输出 undefined this 继承自外层,可能是全局 window/global obj.greet1() // 输出 张三 this 指向 obj6、事件处理函数中的 this
在 DOM 事件处理函数 中(如:click,focus),this 通常指向触发事件的 DOM 元素。若使用箭头函数 ,this 则会继承外层上下文。
javascriptbutton.addEventListener('click', function() { console.log(this) // this 指向 button 元素 }) button.addEventListener('click', () => { console.log(this) // this 继承外层上下文 })7、常见问题与解决方案
问题:回调函数丢失 this 绑定
解决方案:使用 bind 或 箭头函数 保留上下文
javascriptconst obj = { name: '张三', delayedGreet: function() { setTimeout(function() { console.log(this.name); }.bind(this), 100); // 或 setTimeout(() => { console.log(this.name); }, 100) } };问题:嵌套函数 中的 this 不符合预期
解决方法:在 外层中保存 this 引用 或 使用箭头函数
javascriptconst obj = { name: 'Eve', outer: function() { const that = this; function inner() { console.log(that.name); } // 活 const inner = () => { console.log(that.name) } inner(); } };
2、面向对象编程(OOP)
Js 支持面向对象编程(OOP) ,主要通过构造函数 、原型链 和 ES6 的类语法 实现。面向对象的核心概念包括封装、继承和多态。
1、构造函数与原型
使用构造函数创建对象实例,通过原型共享方法和属性:
javascriptfunction Person(name, age) { this.name = name; this.age = age; } Person.prototype.greet = function() { console.log(`Hello, my name is ${this.name}`); }; const person1 = new Person('Alice', 25); person1.greet(); // 输出: Hello, my name is Alice2、ES6 类语法
ES6 引入了
class关键字,简化了面向对象编程:
javascriptclass Person { constructor(name, age) { this.name = name; this.age = age; } greet() { console.log(`Hello, my name is ${this.name}`); } } const person2 = new Person('Bob', 30); person2.greet(); // 输出: Hello, my name is Bob3、核心概念
封装:通过闭包或私有字段实现封装,保护内部数据
javascriptclass Counter { #count = 0; // 私有字段 increment() { this.#count++; } getCount() { return this.#count; } } const counter = new Counter(); counter.increment(); console.log(counter.getCount()); // 输出: 1继承:通过
extends实现继承,子类可以继承父类的属性和方法
javascriptclass Student extends Person { constructor(name, age, grade) { super(name, age); this.grade = grade; } study() { console.log(`${this.name} is studying in grade ${this.grade}`); } } const student1 = new Student('Charlie', 18, 12); student1.greet(); // 输出: Hello, my name is Charlie student1.study(); // 输出: Charlie is studying in grade 12多态:通过方法重写实现多态,子类可以覆盖父类的方法
javascriptclass Animal { speak() { console.log('Animal makes a sound'); } } class Dog extends Animal { speak() { // 重写的方法名需与父类一致 console.log('Dog barks'); } } const animal = new Animal(); const dog = new Dog(); animal.speak(); // 输出: Animal makes a sound dog.speak(); // 输出: Dog barks4、静态方法与属性
使用
static关键字定义 静态方法 和 属性 ,它们属于类本身而非实例:
javascriptclass MathUtils { static PI = 3.14159; static add(a, b) { return a + b; } } console.log(MathUtils.PI); // 输出: 3.14159 console.log(MathUtils.add(2, 3)); // 输出: 55、组合与聚合
通过对象组合实现更灵活的代码复用
javascriptclass Engine { start() { console.log('Engine started'); } } class Car { constructor() { this.engine = new Engine(); } start() { this.engine.start(); console.log('Car started'); } } const car = new Car(); car.start(); // 输出: Engine started \n Car started
3、原型和原型链
1、概念
在 Js 中,每个对象都有一个原型 (
prototype),原型本身也是一个对象。原型链 是通过原型对象的链接 形成的机制,用于实现继承和属性查找。2、原型的作用
对象的原型用于 共享属性和方法 。当访问对象的属性或方法时,如果对象本身没有该属性,引擎会沿着原型链向上查找,直到找到该属性或到达原型链的末尾(
null)。3、原型链的构成
原型链 由多个对象的原型链接而成。例如:
- 对象A的原型是对象B。
- 对象B的原型是对象C。
- 对象C的原型是
null。当访问对象A 的某个属性时,引擎会依次在A、B、C 中查找,直到找到属性或返回
undefined。4、原型的设置方式
- 构造函数与原型
javascriptfunction Person(name) { this.name = name; } Person.prototype.sayHello = function() { console.log(`Hello, ${this.name}`); }; const person = new Person('Alice'); person.sayHello(); // 输出: Hello, Alice
- 直接设置对象的原型
javascriptconst parent = { greet: function() { console.log('Hi!'); } }; const child = Object.create(parent); child.greet(); // 输出: Hi!5、原型链的终点
所有原型链的终点是
Object.prototype,其原型为null。如果属性在整条链中未找到,则返回undefined。6、检查原型的方法
Object.getPrototypeOf()
javascriptconst obj = {}; console.log(Object.getPrototypeOf(obj) === Object.prototype); // true
instanceof运算符
javascriptfunction Animal() {} const dog = new Animal(); console.log(dog instanceof Animal); // true7、修改原型的影响
直接修改原型会影响所有继承该原型的对象
javascriptArray.prototype.customMethod = function() { return 'Custom'; }; const arr = [1, 2, 3]; console.log(arr.customMethod()); // 输出: Custom8、原型链的注意事项
- 避免在运行时频繁修改原型,可能导致性能问题。
- 原型链过长会增加属性查找时间。
- 使用
Object.create(null)可以创建无原型的对象,避免原型链干扰。
4、基于函数的面向对象实现
在 Js 中,函数可以作为构造函数来创建对象实例,实现面向对象编程的核心概念(如封装、继承和多态)。以下是几种常见的实现方式:
1、构造函数模式
通过普通函数定义构造函数,使用 new
关键字实例化对象。构造函数内部的 this指向新创建的对象实例。
javascriptfunction Person(name, age) { this.name = name; this.age = age; this.sayHello = function() { console.log(`Hello, I'm ${this.name}`); }; } const person1 = new Person('Alice', 25); person1.sayHello(); // 输出: Hello, I'm Alice2、原型链继承
利用原型对象(prototype)实现方法共享,避免每次实例化都重复创建方法。通过修改构造函数的
prototype属性实现继承。
javascriptfunction Animal(name) { this.name = name; } Animal.prototype.speak = function() { console.log(`${this.name} makes a noise.`); }; function Dog(name) { Animal.call(this, name); // 调用父类构造函数 } Dog.prototype = Object.create(Animal.prototype); Dog.prototype.constructor = Dog; Dog.prototype.speak = function() { console.log(`${this.name} barks.`); // 方法重写 }; const dog = new Dog('Rex'); dog.speak(); // 输出: Rex barks.3、工厂函数模式
不使用
new关键字,而是通过普通函数返回新对象。适合需要灵活控制对象创建的场景。
javascriptfunction createPerson(name) { const obj = {}; obj.name = name; obj.greet = function() { console.log(`Hi, I'm ${obj.name}`); }; return obj; } const person2 = createPerson('Bob'); person2.greet(); // 输出: Hi, I'm Bob4、闭包实现私有成员
通过IIFE(立即执行函数表达式) 和闭包模拟私有变量,实现数据封装。
javascriptconst Counter = (function() { let privateCount = 0; // 私有变量 function changeBy(val) { privateCount += val; } return { increment: function() { changeBy(1); }, value: function() { return privateCount; } }; })(); Counter.increment(); console.log(Counter.value()); // 输出: 15、class关键字
ES6 引入的
class本质仍是基于原型的语法糖,但提供了更清晰的面向对象写法。
javascriptclass Rectangle { constructor(height, width) { this.height = height; this.width = width; } get area() { return this.calcArea(); } calcArea() { return this.height * this.width; } } const square = new Rectangle(10, 10); console.log(square.area); // 输出: 1006、注意事项
- 使用构造函数时务必通过
new调用,否则this会指向全局对象(严格模式下为undefined)- 原型方法共享可节省内存,但需注意引用类型属性的共享问题
- 现代项目推荐使用
class语法,但需理解其底层仍是基于原型的实现
5、es6中的面向对象
ES6 引入了 class 语法,简化了 JavaScript 的面向对象编程(OOP)实现方式。以下是 ES6 中面向对象的核心特性:
1、类(Class)定义
使用
class关键字定义类,构造函数通过constructor方法声明:
javascriptclass Person { constructor(name, age) { this.name = name; this.age = age; } }2、方法定义
类方法直接写在类内部,无需
function关键字:
javascriptclass Person { greet() { return `Hello, ${this.name}!`; } }3、继承(Inheritance)
通过
extends实现继承,super调用父类构造函数或方法:
javascriptclass Student extends Person { constructor(name, age, grade) { super(name, age); // 调用父类构造函数 this.grade = grade; } }4、静态方法(Static Methods)
用
static定义类方法,直接通过类名调用:
javascriptclass MathUtils { static square(x) { return x * x; } } MathUtils.square(3); // 95、Getter/Setter
通过
get和set定义属性访问器:
javascriptclass Rectangle { constructor(width, height) { this._width = width; this._height = height; } get area() { return this._width * this._height; } }6、私有字段(ES2022+)
使用
#前缀定义私有字段(需现代浏览器或 Node.js 支持):
javascriptclass Counter { #count = 0; // 私有字段 increment() { this.#count++; } }7、原型链与 ES6 类的本质
ES6 的
class本质仍是基于原型的语法糖。以下两种写法等价:
javascript// ES6 类 class Foo { bar() {} } // ES5 等效实现 function Foo() {} Foo.prototype.bar = function() {};8、注意事项
- 类声明不会提升(必须先定义后使用)。
- 类方法不可枚举(不同于 ES5 构造函数原型方法)。
- 构造函数必须通过
new调用,否则报错。