深入解析 JavaScript 构造函数:特性、用法与原型链

news2024/10/24 22:20:37

在 JavaScript 中,构造函数是实现面向对象编程的关键工具之一。它与 this 关键字、箭头函数的作用域链以及原型和原型链紧密相关。本文将全面深入地探讨 JavaScript 构造函数的各个方面。

一、构造函数的定义与用法

构造函数是一种特殊的函数,用于创建对象。其名称通常以大写字母开头,以区别于普通函数。例如:

function Person(name, age) {
    this.name = name;
    this.age = age;
}

使用 new 关键字调用构造函数可以创建对象实例。例如:

let person1 = new Person('Alice', 30);
let person2 = new Person('Bob', 25);
console.log(person1); // { name: 'Alice', age: 30 }
console.log(person2); // { name: 'Bob', age: 25 }

二、构造函数的特性

(一)创建对象实例

当使用 new 调用构造函数时,会自动创建一个新对象,在构造函数内部通过 this 引用该对象。

(二)属性初始化

构造函数可以接受参数来初始化对象的属性,根据不同输入创建具有不同属性值的对象。例如:

function Rectangle(width, height) {
    this.width = width;
    this.height = height;
}
const rect = new Rectangle(5, 10);
console.log(rect); // { width: 5, height: 10 }

(三)方法继承

可以在构造函数内部定义方法,这些方法会被创建的对象实例继承。例如:

function Animal(name) {
    this.name = name;
    this.sayName = function() {
        console.log(`My name is ${this.name}.`);
    };
}
const animal = new Animal('Cat');
animal.sayName(); // My name is Cat

(四)原型共享

每个构造函数都有一个 prototype 属性指向原型对象,原型对象上的属性和方法可被该构造函数创建的所有对象实例共享。例如:

function Person() {}
Person.prototype.sayHello = function() {
    console.log('Hello!');
};
const person1 = new Person();
const person2 = new Person();
person1.sayHello(); // Hello!
person2.sayHello(); // Hello!

(五)可扩展性

通过在构造函数的原型上添加属性和方法,可以扩展对象,无需修改构造函数本身。例如:

function Shape() {}
Shape.prototype.draw = function() {
    console.log('Drawing a shape.');
};
function Circle() {}
Circle.prototype = Object.create(Shape.prototype);
Circle.prototype.constructor = Circle;
Circle.prototype.drawCircle = function() {
    console.log('Drawing a circle.');
};
const circle = new Circle();
circle.draw(); // Drawing a shape.
circle.drawCircle(); // Drawing a circle.

(六)与 new 关键字的关系

构造函数必须与 new 一起使用,否则可能导致意外结果,如 this 指向全局对象。例如:

function Person(name) {
    this.name = name;
}
const person = Person('Alice');
console.log(person); // undefined
console.log(window.name); // 'Alice'

const properPerson = new Person('Bob');
console.log(properPerson); // { name: 'Bob' }
console.log(properPerson.name); // 'Bob'

三、构造函数中的 this 关键字

(一)指向新创建的对象实例

在构造函数被调用时,this 指向正在创建的新对象,可用于为新对象添加属性和方法。例如:

function Person(name, age) {
    this.name = name;
    this.age = age;
    this.sayHello = function() {
        console.log(`Hello, I am ${this.name}, ${this.age} years old.`);
    };
}
const person1 = new Person('Alice', 30);
person1.sayHello(); // Hello, I am Alice, 30 years old.

(二)方法调用中的 this

当对象的方法被调用时,this 通常指向该对象。例如:

const obj = {
    message: 'Hello',
    printMessage: function() {
        console.log(this.message);
    }
};
obj.printMessage(); // Hello

(三)丢失上下文

某些情况下,this 的指向可能会丢失,如将对象方法赋值给变量后调用。例如:

const obj = {
    message: 'Hello',
    printMessage: function() {
        console.log(this.message);
    }
};
const func = obj.printMessage;
func(); // undefined(在严格模式下会报错)

(四)使用 callapply 和 bind 方法改变 this 指向

1. 解释

在 JavaScript 中,callapply 和 bind 方法都是用于改变函数执行时的 this 指向的。这在需要动态地确定函数内部 this 的指向时非常有用,尤其是在回调函数、面向对象编程中的方法调用等场景中。

2. call 方法

  • 语法function.call(thisArg, arg1, arg2,...)
  • 作用:立即调用一个函数,并指定函数内部的 this 指向为 thisArg,后面可以跟多个参数依次传递给被调用的函数。
  • 示例
function greet() {
    console.log(`Hello, I am ${this.name}.`);
}
const person = { name: 'Bob' };
greet.call(person); // Hello, I am Bob.

3. apply 方法

  • 语法function.apply(thisArg, [argsArray])
  • 作用:与 call 类似,也是立即调用一个函数并指定 this 指向为 thisArg,但参数是以数组的形式传递。
  • 示例
function sum(a, b) {
    return this.value + a + b;
}
const obj = { value: 10 };
const result = sum.apply(obj, [5, 3]);
console.log(result); // 18

4. bind 方法

  • 语法const newFunction = function.bind(thisArg, arg1, arg2,...)
  • 作用:创建一个新函数,这个新函数的 this 被永久地绑定到指定的 thisArg 上,并且可以预先传递一些参数。新函数可以在以后被调用,并且其 this 指向不会再改变。
  • 示例
function greet() {
    console.log(`Hello, I am ${this.name}.`);
}
const person = { name: 'Charlie' };
const boundGreet = greet.bind(person);
boundGreet(); // Hello, I am Charlie.

5. 区别总结

  • call 和 apply 都是立即执行函数,而 bind 是返回一个新函数,不会立即执行。
  • call 和 apply 的主要区别在于传递参数的方式,call 是依次列出参数,apply 是通过数组传递参数。

四、箭头函数的作用域链

(一)与普通函数作用域链的相似之处

  • 遵循词法作用域规则,函数定义时确定可访问的变量范围。例如:
const outerVariable = 'outer';
function outerFunction() {
    const innerVariable = 'inner';
    const arrowFunc = () => {
        console.log(outerVariable);
        console.log(innerVariable);
    };
    arrowFunc();
}
outerFunction(); // outer inner

  • 箭头函数可嵌套在普通函数或其他箭头函数中,作用域链反映嵌套关系。例如:
function outer() {
    const outerVar = 'outer';
    function middle() {
        const middleVar = 'middle';
        const innerArrow = () => {
            console.log(outerVar);
            console.log(middleVar);
        };
        innerArrow();
    }
    middle();
}
outer(); // outer middle

(二)箭头函数作用域链的特殊之处

  • 不绑定自己的 thisargumentssuper 或 new.target,而是继承外层函数的值。例如:
const obj = {
    value: 10,
    method: function() {
        const arrowFunc = () => {
            console.log(this.value);
        };
        arrowFunc();
    }
};
obj.method(); // 10

  • 简洁的语法使作用域链更清晰,在回调函数等场景中可避免 this 指向问题。例如:
const numbers = [1, 2, 3];
numbers.forEach((num) => {
    console.log(num);
}); // 1 2 3

(三)作用域链与执行顺序的关系

作用域链决定了代码的执行顺序,JavaScript 引擎根据作用域链查找变量和函数。例如:

function outerFunction() {
    const outerVariable = 'outer';
    function innerFunction() {
        console.log(outerVariable);
    }
    return innerFunction;
}
const newFunction = outerFunction();
newFunction(); // outer
console.log(outerVariable); // 报错:ReferenceError: outerVariable is not defined(在全局作用域中无法访问 outerVariable)

五、构造函数的使用场景

(一)创建对象实例

用于模拟面向对象编程,创建具有相似结构和行为的多个对象。例如:

function Character(name, health, attackPower) {
    this.name = name;
    this.health = health;
    this.attackPower = attackPower;
    this.attack = function(target) {
        target.health -= this.attackPower;
        console.log(`${this.name} attacks ${target.name}. ${target.name}'s health is now ${target.health}.`);
    };
}
const player = new Character('Player 1', 100, 10);
const enemy = new Character('Enemy 1', 80, 8);
player.attack(enemy); // Player 1 attacks Enemy 1. Enemy 1's health is now 72.

(二)封装数据和行为

可通过构造函数和闭包模拟一定程度的封装,保护内部数据。例如:

function Counter() {
    let count = 0;
    this.increment = function() {
        count++;
        console.log(count);
    };
    this.decrement = function() {
        count--;
        console.log(count);
    };
}
const counter = new Counter();
counter.increment(); // 1
counter.increment(); // 2
counter.decrement(); // 1

(三)模块模式

结合闭包实现模块模式,创建单例对象或封装特定功能的模块。例如:

function Logger() {
    const logs = [];
    function addLog(message) {
        logs.push(message);
        console.log(`Logged: ${message}`);
    }
    function getLogs() {
        return logs;
    }
    return {
        addLog,
        getLogs
    };
}
const logger = new Logger();
logger.addLog('First log message'); // Logged: First log message
logger.addLog('Second log message'); // Logged: Second log message
console.log(logger.getLogs()); // ['First log message', 'Second log message']

(四)继承和扩展

通过原型链实现继承,创建子类并继承父类的属性和方法。例如:

function Shape() {
    this.draw = function() {
        console.log('Drawing a shape.');
    };
}
function Circle(radius) {
    Shape.call(this);
    this.radius = radius;
    this.drawCircle = function() {
        console.log(`Drawing a circle with radius ${this.radius}.`);
    };
}
Circle.prototype = Object.create(Shape.prototype);
Circle.prototype.constructor = Circle;
const circle = new Circle(5);
circle.draw(); // Drawing a shape.
circle.drawCircle(); // Drawing a circle with radius 5.

(五)事件处理和状态管理

管理状态和处理事件,如创建具有特定状态和事件处理方法的组件。例如:

function Button(text) {
    this.text = text;
    this.isPressed = false;
    this.clickHandler = function() {
        this.isPressed =!this.isPressed;
        console.log(`Button ${this.text} is ${this.isPressed? 'pressed' : 'released'}.`);
    };
}
const button = new Button('Click me');
button.clickHandler(); // Button Click me is pressed.
button.clickHandler(); // Button Click me is released.

六、构造函数的原型和原型链

每个构造函数都有一个 prototype 属性指向原型对象。原型上的属性和方法可被对象实例共享。例如:

function Person() {}
Person.prototype.sayHello = function() {
    console.log('Hello!');
};
const person1 = new Person();
const person2 = new Person();
person1.sayHello(); // Hello!
person2.sayHello(); // Hello!

原型链是 JavaScript 实现继承的机制,当访问对象的属性或方法时,若对象本身找不到,会沿着原型链向上查找,直到找到或到达顶端。例如:

function Animal() {}
Animal.prototype.sayHello = function() {
    console.log('Hello from Animal!');
};
function Dog() {}
Dog.prototype = Object.create(Animal.prototype);
const dog = new Dog();
dog.sayHello(); // Hello from Animal!

若这篇文章对你有启发,点赞可鼓励作者并让更多人看到,收藏以便随时查阅,关注作者能第一时间获取更多编程知识分享。让我们在编程海洋中探索进步,期待你的点赞、收藏和关注,携手书写精彩篇章!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2222749.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Acrel-1000变电站综合自动化系统及微机在化工企业中的应用方案

文:安科瑞郑桐 摘要:大型化工企业供配电具有的集约型特点,化工企业内35kV变电站和10kV变电所数量大、分布广,对于老的大多大型及中型化工企业而言,其变电站或变电所内高压电气设备为旧式继电保护装置,可靠…

详解Java的类文件结构(.class文件的结构)

this_class 指向常量池中索引为 2 的 CONSTANT_Class_info。super_class 指向常量池中索引为 3 的 CONSTANT_Class_info。由于没有接口,所以 interfaces 的信息为空。 对应 class 文件中的位置如下图所示。 06、字段表 一个类中定义的字段会被存储在字段表&#x…

zotero文献管理学习

1 zotero软件简介 zotero是一款开源的文献管理软件。如果你听说或使用过EndNote,那么可能会对“文献管理”有一定的概念。可以简单地这样理解:zotero一定程度上可以作为EndNote的平替。 EndNote需要注册付费,对于无专业科研机构隶属关系的企…

MATLAB运动目标检测系统

应用背景 运动目标的定位跟踪,检测识别,运动分析在图像压缩、运动分析、交通检测,智能监控等方面有主要的应用。 首先,在图像压缩中,运动目标检测技术可以在背景区域中将前景区域提取分割出来,只传递部分…

植物端粒到端粒(T2T)基因组研究进展与展望

鼠鼠跳槽了,因为现在公司发(bu)展(zhang)受(gong)限(zi),只能跳一次,从大兴到昌平了。从二代ivd行业去三代T2T和泛基因组了。在这里我们分享一篇文章。 摘要:高质量的参考基因组是基因组学研究的基础。目前,大多数的参…

笨蛋学习FreeMarker

笨蛋学习FreeMarker FreeMarker参考网址创建实例引入Maven创建工具类创建实例并进行输出 FreeMarker数据类型布尔型:日期型:数值型:字符型:需要处理字符串为null的情况,否则会报错字符串为空不会报错cap_firstuncap_fi…

【银河麒麟高级服务器操作系统实例】金融行业TCP连接数猛增场景的系统优化

了解更多银河麒麟操作系统全新产品,请点击访问 麒麟软件产品专区:https://product.kylinos.cn 开发者专区:https://developer.kylinos.cn 文档中心:https://documentkylinos.cn 服务器环境以及配置 物理机/虚拟机/云/容器 物理…

12 django管理系统 - 注册与登录 - 登录

为了演示方便&#xff0c;我就直接使用models里的Admin来演示&#xff0c;不再创建用户模型了。 ok&#xff0c;先做基础配置 首先是在base.html中&#xff0c;新增登录和注册的入口 <ul class"nav navbar-nav navbar-right"><li><a href"/ac…

使用 VSCode 通过 Remote-SSH 连接远程服务器详细教程

使用 VSCode 通过 Remote-SSH 连接远程服务器详细教程 在日常开发中&#xff0c;许多开发者需要远程连接服务器进行代码编辑和调试。Visual Studio Code&#xff08;VSCode&#xff09;提供了一个非常强大的扩展——Remote-SSH&#xff0c;它允许我们通过 SSH 协议直接连接远程…

一图读懂“低空经济”

&#x1f482; 个人主页: 同学来啦&#x1f91f; 版权: 本文由【同学来啦】原创、在CSDN首发、需要转载请联系博主 &#x1f4ac; 如果文章对你有帮助&#xff0c;欢迎关注、点赞、收藏和订阅专栏哦 文章目录 ✈️ 一、低空经济简介&#x1f534; 1、基本含义&#x1f7e0; 2、…

【免费领取】基于javaweb实现的的日志管理系统

主营内容&#xff1a;SpringBoot、Vue、SSM、HLMT、Jsp、PHP、Nodejs、Python、爬虫、数据可视化、小程序、安卓app等设计与开发。 收藏点赞不迷路 关注作者有好处 文末获取源码 项目描述 本工作日志管理系统是一个面向中小企业的简单的工作管理系统&#xff0c;它主要实现公…

【Python】Pandas基础操作手册(上)

哈喽&#xff0c;哈喽&#xff0c;大家好~ 我是你们的老朋友&#xff1a;保护小周ღ 今天给大家带来的是【Python】Pandas基础操作手册&#xff08;上&#xff09;本次主要讲解, python pandas 模块的一些基本概念, 以及了解 Dataframe 对象的创建, 赋值, 保存. 一起来看看叭…

【SpringBoot】17 多文件上传(Thymeleaf + MySQL)

Git仓库 https://gitee.com/Lin_DH/system 文件上传 可参考上一篇【SpringBoot】16 文件上传&#xff08;Thymeleaf MySQL&#xff09; https://blog.csdn.net/weixin_44088274/article/details/143004298 介绍 文件上传是指将本地的图片、视频、音频等文件上传到服务器&…

php生成PDF文件(FPDF)

FPDF即“Free PDF”&#xff0c;FPDF类库提供了基本的PDF创建功能&#xff0c;其源代码和使用权是免费的。 PDF格式文档优势 通用&#xff1a;PDF文档在UNIX和Windows系统均可正常使用。 安全&#xff1a;PDF文档可设置为只读模式&#xff0c;并且可以添加密码等保护措施。 美…

【PDF文件】默认被某种软件打开,如何进行修改?

当有时下载某种软件后&#xff0c;电脑中的PDF文件就默认由该种软件打开&#xff0c;每次需要右键选择打开方式才能选择需要的其他软件打开。如下图所示。 修改方法&#xff1a; &#xff08;1&#xff09;点击电脑的“设置”&#xff0c;选择应用 &#xff08;2&#xff09;…

深入浅出理解BLE AUDIO CSIS

CSIS是Coordinate Sets Identification service,翻译过来就是协调集识别服务。什么是协调集&#xff0c;可以理解为具有相同特征的一伙设备&#xff0c;最典型的就是左右两个蓝牙耳机是一个协调集&#xff0c;所以它们具有相同的协调集标志&#xff0c;但是具有相同协调集的设备…

disql 全量备份SQL脚本DM7/DM8

disql 全量备份SQL脚本DM7/DM8 环境介绍1 全量备份前准备工作2 全量备份2.1 cd 到数据库bin 目录,并编辑文件2.2 编写数据库全量备份SQL 脚本2.3 执行编写的sql脚本2.4 编写Linux定时任务 , 每月执行全量备份 3 备份还原4 增量备份配置5 更多达梦数据库学习使用列表 环境介绍 …

CTA-GAN:基于生成对抗网络对颈动脉和主动脉的非增强CT影像进行血管增强

写在前面 目前只分析了文章的大体内容和我个人认为的比较重要的细节&#xff0c;代码实现还没仔细看&#xff0c;后续有时间会补充代码细节部分。 文章地址&#xff1a;Generative Adversarial Network-based Noncontrast CT Angiography for Aorta and Carotid Arteries 代…

【C++】STL容器-string常用接口

1.string类的优势及重要性&#xff08;部分&#xff09; C语言中&#xff0c;字符串是以’\0’结尾的一些字符的集合&#xff0c;为了操作方便&#xff0c;C标准库中提供了一些str系列的库函数&#xff0c;但是这些库函数与字符串是分离开的&#xff0c;不太符合OOP的思想&…

【Unity实战笔记】第二一 · 基于状态模式的角色控制——以UnityChan为例

目录 一 内容摘要二 前言三 状态模式的必要性3.1 非状态模式的角色控制3.2 简易状态模式的角色控制3.3 状态模式3.3.1 IState3.3.2 IdleState3.3.3 RunState3.3.4 JumpState3.3.5 PlayerController_ComplexStateMode3.3.6 注意事项 3.4 SMB 四 基于SMB的角色控制4.1 项目实战案…