首页 > 其他分享 >调用了这么久的JS方法是长在对象、类、值本身还是原型链上?

调用了这么久的JS方法是长在对象、类、值本身还是原型链上?

时间:2024-06-20 21:59:00浏览次数:22  
标签:调用 console log 对象 这么久 链上 const JS 方法

调用了这么久的JS方法是长在对象、类、值本身还是原型链上?

JavaScript这门语言总是能带给我惊喜,在敲代码的时候习以为常的写法,退一步再看看发现自己其实对很多基操只有表面的使用,而从来没思考过为何要这样操作。

今天整理JS代码的时候突然发出灵魂三连问:

  • 为什么有些时候操作对象,可以直接调用对象上的方法,但有些时候我们使用类似Array.from()的写法?
  • 在对象上调用的方法跟在原型上调用的方法区别是什么?这两者相同么?
  • 为什么JS上可以直接在基础类型值上调用对象上面才存在的方法?基础类型值上调用的方法与在对象上调用的方法有区别么?

不同的方法调用方式

瞟了眼我的代码,立马就发现了一个调用类上方法的片段:

const obj = { a: 1 };
console.log(Object.hasOwn(obj, 'a')); // true
// 但是如果在对象上调用,则会抛不存在的错误
console.log(obj.hasOwn('a')); // TypeError: obj.hasOwn is not a function

在上面的例子里,Object.hasOwn是一个可以直接调用的方法,但令人困惑的是,当我们尝试直接在对象实例上调用hasOwn方法时,却抛出了一个类型错误,是不是有点反直觉? 我仔细想了一想突然发现,其实这只是一个基础JS概念的一个外在表现,只不过我们习惯了作为现象使用它,却很少会想到它背后的逻辑。

静态方法与实例方法

其实,我们需要做的只是区分JavaScript静态方法实例方法

静态方法 是定义在类上的方法,而不是在类的实例上,静态方法内部访问不到this与实例变量。所以我们只能通过类来调用这些方法,而不能通过一个实例来调用

class MyClass {
  static staticMethod() {
    console.log('这是个静态方法');
  }
}

MyClass.staticMethod(); // 正常执行
const myInstance = new MyClass();
myInstance.staticMethod(); // Error: myInstance.staticMethod is not a function

实例方法 是定义在类的原型上的方法,实例方法内可以访问对象的属性,也可以访问this,可以直接在实例化对象上调用这些方法

class MyClass {
  instanceMethod() {
    console.log('这是个实例/对象方法');
  }
}

const myInstance = new MyClass();
myInstance.instanceMethod(); // 正常执行

概括来说,上面例子中Object.hasOwn()是一个需要传参的、在Object这个类上的静态方法,所以才需要在类上直接调用,而不能在实例对象上调用;但在例如arr.sort()的调用,实际调用的是实例对象上的方法

至于为何会做如此区分,原因是一个简单的面向对象编程需求:如果一个方法逻辑不涉及对象上的属性,但又逻辑上属于这个类,通过接受参数就可以实现功能的,则可以作为一个类的静态方法存在。但如果它需要直接访问类上属性,直接作为实例方法显然更加妥当。

原型链与方法调用

JavaScript中的每个对象都有一个原型(prototype)(除了Object.protoype也就是所有原型的尽头),对象的方法实际上是定义在原型链上的。虽然我们可能是在对象上调用了一个方法,实际上JavaScript引擎会沿着原型链查找该方法并调用。

const arr = [1, 2, 3];
console.log(arr.join('-')); // "1-2-3"
console.log(Array.prototype.join.call(arr, '-')); // "1-2-3"

上面的例子里,join方法是数组的实例方法。实例方法可以直接在数组的实例上调用,也可以通过Array.prototype.join.call的方式来调用,这俩本质上是一样的。唯一区别是Array.prototype.join.call允许我们在任何类似数组的对象上调用这个方法,哪怕它不是一个真正的数组。
等等?我们可以在不是数组的值上调用join?是的

const pseudoArray = { 0: 'one', 1: 'two', 2: 'three', length: 3 };

// ❌显然object上没有join方法,这样调用会报错
pseudoArray.join(','); // Error: pseudoArray.join is not a function

// 成功在object上调用join!!
const result = Array.prototype.join.call(pseudoArray, ',');
console.log(result); // "one,two,three"

所以,在对象上调用实例方法,等同于按照这个对象的原型链一层一层向父类上找同名方法来调用。

基础类型的自动包装

虽然其他支持面向对象编程范式的语言也有类似行为,也就是对基本类型的自动包装自动拆包,但为了百分百掌握JavaScript的行为与他们的异同,还是再来确定一遍吧

每当我们在基本类型值上(例如"hello"6)上调用方法,JavaScript引擎都会先使用基本类型对应的包装类型对值进行包装,调用对应的方法,最后将包装对象丢掉还原基础类型。这是个引擎内部的隐式操作,所以我们没有任何的感知。

JavaScript对于以下的基本类型,都有对应的包装类型。可以通过typeof操作结果是基本类型名还是object来确认:

  • string - String
  • number - Number
  • boolean - Boolean
  • symbol - Object
  • bigint - Object

让我们列一下他们基本类型对应包装类型的使用:

// string
const primitiveString = "hello";
const objectString = new String("hello");
console.log(typeof primitiveString); // "string"
console.log(typeof objectString); // "object"

// number
const primitiveNumber = 42;
const objectNumber = new Number(42);
console.log(typeof primitiveNumber); // "number"
console.log(typeof objectNumber); // "object"

// boolean
const primitiveBoolean = true;
const objectBoolean = new Boolean(true);
console.log(typeof primitiveBoolean); // "boolean"
console.log(typeof objectBoolean); // "object"

// symbol
const primitiveSymbol = Symbol("description");
const objectSymbol = Object(primitiveSymbol);
console.log(typeof primitiveSymbol); // "symbol"
console.log(typeof objectSymbol); // "object"

// bigint
const primitiveBigInt = 123n;
const objectBigInt = Object(primitiveBigInt);
console.log(typeof primitiveBigInt); // "bigint"
console.log(typeof objectBigInt); // "object"

所以,在基本类型上调用方法,等同于创建这个基本类型对应的包装类型的对象并调用方法,最后拆包并返回原始类型的值。本质上还是调用了同类型包装行为创建的对象上的方法。

"str".toUpperCase();
// 等同于
(new String("str")).toUpperCase()
// 当然,这里巧了,toUpperCase()本来也没想返回包装类型的对象

结语

哈哈,原来这个类、对象方法调用现象的原因其实一直都在我的大脑里,这只是面向对象编程中的一个很稀松平常的事实,但平时从来只是使用,还从来没联想过为何他会这样。

不知道你有没有感受到这种编程语言带来的实践经验与基础理论交融的乐趣。在一点点的实践中才会慢慢发现原来看似“这样写就能跑”的一些代码,其实背后都有曾经学习、分析过的程序概念和理论的支撑。这种感受或许就是编程快乐的其中之一个源头吧。

为大家的好奇心与耐心致敬。

标签:调用,console,log,对象,这么久,链上,const,JS,方法
From: https://www.cnblogs.com/camwang/p/18259567

相关文章

  • 超级好用的JSON格式化可视化在线工具
    JSON是开发非常常用的一种报文格式,最常见的需求就是将JSON进行格式化,最好是有图形化界面显示结构关系,以便进行数据分析。理想的在线JSON工具,应该支持快速格式化、可压缩、快捷复制、可下载导出,对存在语法错误的地方能明确显示。提供搜索定位功能,可视区足够大,最好支持全屏。......
  • nodejs Sequelize6连接mysql8的配置怎么写
    官方文档的构造器很多,这里仅展示一种const{Sequelize}=require('sequelize');/** *dbname *username *password *options */constsequelize=newSequelize('dbname','username','password',{  host:'127.0.0.1&......
  • jsQR库识别二维码
    <html><head><metacharset="utf-8"/><title>jsQR库识别二维码</title><scriptsrc="https://cdn.bootcss.com/jquery/3.4.1/jquery.min.js"></script><scriptsrc=&q......
  • VueJs导出Excel,插件 xlsx
    1.安装组件:cnpminstallxlsx--save2.新建一个辅助ts、js文件我的excelUtil.ts注意:新版本的插件导入写法变了:import*asXLSXfrom'xlsx'import{timestampToDateString}from'@/common/dateUtil'import*asXLSXfrom'xlsx'//headerColumns:属性列表//header......
  • 基于Vue+Nodejs实现医药商城销售系统
    作者简介:Java领域优质创作者、CSDN博客专家、CSDN内容合伙人、掘金特邀作者、阿里云博客专家、51CTO特邀作者、多年架构师设计经验、腾讯课堂常驻讲师主要内容:Java项目、Python项目、前端项目、人工智能与大数据、简历模板、学习资料、面试题库、技术互助收藏点赞不迷路 ......
  • three.js 第六节 -纹理和贴图(普通贴图、hdr贴图)
    素材//@ts-nocheck//引入three.jsimport*asTHREEfrom'three'//导入轨道控制器import{OrbitControls}from'three/examples/jsm/controls/OrbitControls'//导入lil.guiimport{GUI}from'three/examples/jsm/libs/lil-gui.module.min.js&......
  • js 如何解析带有html标签的内容
    在JavaScript中解析带有HTML标签的内容,通常是为了提取信息或转换HTML结构为可操作的对象。以下几种方式可以实现:1.使用DOMParser        如果想将HTML字符串解析成DOM节点以便进一步操作(比如查询、修改等),可以使用DOMParser。这是一个非常强大的工具,特别是在处理复......
  • 绝杀 GETPOST 嵌套的 JSON 参数
    JSON(JavaScriptObjectNotation)是一种轻量级的数据交换格式,常用于Web应用程序中的数据传输。在HTTP数据包信息传递时,JSON扮演着非常正常的角色,因为它是一种通用的数据格式,可以被多种编程语言和应用程序所支持。当客户端向服务器发送HTTP请求时,请求头中可以指定请求体的数据......
  • vue(vue.js) 监视属性与深度监视
    原文链接:vue(vue.js)监视属性与深度监视–每天进步一点点(longkui.site) 1.监视属性监视属性:监视某个属性的变化小案例:<!DOCTYPEhtml><htmllang="en"><body> <divid="root"><h2>今天天气很{{hot?'热':'冷'}}</h2&g......
  • Nodejs基本概念
     Node.js基本概念前言Node.js是一个基于ChromeV8引擎的JavaScript运行环境,主要用于构建服务器端应用。由于其高效的事件驱动和非阻塞I/O模型,Node.js在处理高并发和实时应用方面具有显著优势。本篇文章将介绍Node.js的基本概念,帮助初学者快速上手。1.什么是No......