前言
本篇内容有点长 , 可能看不完 , 那就收藏起来吧 ~ 🤡
在此前请回答下 js 有什么特性 。
请说说你对 js 语言的理解 ?
JavaScript 是一种运行在浏览器中的主要用于增强网页的动态效果、提高与用户的交互性的编程语言。相比于其他编程语言,它具有许多特点,主要包括以下几方面。
解释性
JavaScript 不同于一些编译性的程序语言,它是一种解释性的程序语言,它的源代码不需要经过编译,直接在浏览器中运行时进行解释。
动态性
JavaScript 是一种基于事件驱动的脚本语言,它不需要经过 Web 服务器就可以对用户的输入直接做出响应。
跨平台性
JavaScript依赖于浏览器本身,与操作环境无关。任何浏览器,只要具有 JavaScript 脚本引擎就可以执行 JavaScript。目前,几乎所有用户使用的浏览器都内置了 JavaScript 脚本引擎。
安全性
JavaScript是一种安全性语言,它不允许访问本地的硬盘,同时不能将数据存到服务器上,不允许对网络文档进行修改和删除,只能通过浏览器实现信息浏览或动态交互。这样可有效地防止数据丢失。
基于对象
JavaScript是一种基于对象的语言,同时也可以被看作是一种面向对象的语言。这意味着它能运用自己已经创建的对象。因此,许多功能可以来自于脚本环境中对象的方法与脚本的相互作用。
请你说说 js 有哪些数据类型 ?
JavaScript 有八种基本数据类型,分为
- 原始类型(Primitive Types)
- 引用类型(Reference Types)
以下是 ECMA262 定义的变量类型
ECMA262是 ECMAScript 语言规范的标准文档。ECMAScript 是一种脚本语言标准,JavaScript 是 ECMAScript 最著名的实现语言之一。这个标准详细规定了脚本语言的语法、类型、语句、关键字、保留字、操作符、对象等诸多方面的内容。
原始类型
undefined
表示变量未初始化。一个变量声明后但未
赋值时,它的默认值是 undefined.
null
表示一个空的值或一个不存在的对象。null 是一
个特殊的关键字,它代表“无值”。
理解 undefine是系统给的 , null 是编码者给的
boolean
只有两个值:true和 false,用于逻辑判断。
表示双精度 64 位二进制格式的浮点数,可以
number
- 表示双精度 64 位二进制格式的浮点数,可以表示整数和浮点数。
- 特殊值包括 NaN(NotaNumber 不是一个数字)和Infinity(无穷大)。
string
表示字符序列,可以用单引号、双引号或反引号括起来的文本。
symbol
用来创建唯一旦不可变的值,主要用于对象属性的唯一标识,避免属性名冲突。
- Symbol 是 es6 的新数据类型, 代表一独一无二 , 可以给数据打上标签 , 使之为"独一无二" !
- 具有函数一样的外观 , 但是是简单数据类型
Symbol 是 ES6 引入的一种新的原始数据类型,它的主要目的是创建独一无二的值。每次调用
Symbol()函数时,都会生成一个全新的、在整个程序运行期间独一无二的 Symbol 值。这个值没有字面量形式(除了通过
Symbol.for()方法创建的可以有类似共享字面量的情况,但这里是单纯的Symbol()),并且它与其他数据类型的值都不相等,即使是连续多次调用Symbol()生成的值相互之间也是不相等的。所以当我们直接在控制台输出Symbol()生成的值时,就会看到这样一个独特的、类似Symbol(description)形式的输出结果,它用于在程序中作为唯一的标识符来区分不同的对象属性、避免命名冲突等用途。
请看下面的代码
console.log(Symbol())
再来看看它独一无二性
console.log(Symbol('1')==Symbol('1'))
BigInt
用于表示任意精度的大整数,允许操作超过Number 能表示的范围的整数。
比如下图
引用类型
Object
- 对象
- 数组
- 函数
- ...
类型检测
- 使用 typeof 检查原始类型 (例如:typeof 123 ===“number”)
let a=1;
//typeof 是js的类型运算符
console.log(typeof a , typeof(a))//number number
2. 使用 instanceof 检查引用类型! (例如:[] instanceof Array === true
- null 是一个特殊情况,typeof null 返回“object”,
这是JavaScript 早期实现中的一个 bug,但被保留了下来。
类型转换如字符串与数字相加时,数字会被转换1)自动类型转换:为字符串。
(后面详细展开为什么)
显式类型转换
使用 Number()、String()、Boolean()等函数将值转换为指定类型 。
原始类型和引用类型的区别 ?
变量内存分配的区别
1)原始类型
- 变量存储在栈(stack)中,值直接保存在变量访问的位置,由于其大小固定且频繁使用,存储在栈中具有更高的性能。
2)引用类型
- 变量储存在栈中 , 值存储在堆(heap)中
赋值方式区别
原始类型
- 复制的是值本身。例如,将一个number 类型的变量赋值给另一个变量,两个变量互不影响。
引用类型
- 复制的是引用(指针)。多个变量引用同一个对象时,一个变量的修改会影响其他变量。
- 比如下面的 obj2
let obj ={
name: 'zhangsan',
job : 'teacher',
company :'字节',
}
obj.hometown = '北京' ;
let a=1;
let b=1;
// let c = a ; // 深拷贝 地址不同
//
// c=2;
//
// console.log(a, c);
let obj2=obj; //浅拷贝 地址相同
obj2.name='lisi';
console.log(a, b);
console.log(obj, obj2)
//所有的变量都会在栈内存中存储,栈内存中存储的是变量名和变量值的地址,变量值存储在堆内存中。
详细解释原始类型与引用类型在变量储存方面的区别
JavaScript的所有变量(包括函数)在整个处理过程中都是存放在内存中的,所以要对一个变量进行处理。
首先得为变量分配内存。
JavaScript内存分配和其他许多语言一样,是根据变量的数据类型来分配内存的,而 JavaScript变量的数据类型由所赋的值的类型决定。
JavaScript支持的数据类型可分为两大类: 基本数据类型和复杂数据类型。
在JavaScript中数组、函数都属于对象类型 (挖个坑,待会填一下) 。除了基本数据类型以外的数据类型全都是对象类型。
在JavaScript 中,
- 基本数据类型变量分配在栈内存中,其中存放了变量的值,对其是按值来访问的;
- 而对象类型的变量则同时会分配栈内存和堆内存,其中栈内存存放的是地址,堆内存存放的是引用类型的值。 可以参考下面的图来形象了解下 !
地址指向堆内存中存放的值。对该变量的访问是按引用来访问的,即首先读取到栈内存存放的地址
然后按该地址找到堆内存读取其中存放的值。JavaScript 之所以按变量的不同数据类型来分配内存,
主要原因是:
- 栈内存比堆内存小,而且栈内存的大小是固定的,而堆内存大小可以动态变化。
- 基本数据类型的值的大小固定,对象类型的值大小不固定,所以将它们分别存放在栈内存和堆内存是合理的。
为什么typeof null的结果是“object" ?
历史局限
JavaScript 在最初设计时,使用了32位系统。为了
优化性能,JavaScript 的值被存储为二进制数据,低位
用来表示数据的类型。
标识符相同
对象的类型标识符是000,而 null被认为是一个空
指针(即零地址),它的二进制表示全是〇,也即
00000000。
由于 null 的二进制表示和对象的类型标识符相同,
typeof null 结果就被错误地设置为“object"。
将错就错 🤡
尽管这个错误很早就被发现,但为了保持向后兼容
性,修复这个错误会导致大量现有代码出错。因此,这个行为被保留下来了。
果然 , bug 是不能搞的 ,一旦改了 ,就全崩了
console.log(typeof null);// 输出:"object
知识补充
判断 null 的正确方法 ("==" 与"==="的区别)
1)直接比较
最简单的方法是直接使用严格相等===进行比较,示
let value = null;
console.log(value===null);// 输出:true
2)使用==比较
使用==也可以,但不推荐,因为它会进行类型转换,
示例如下:
let value = null;
console.log(valuenull);//输出:true
为什么JavaScript 中 0.1+0.2!== 0.3 ? 如何让其相等?
为什么JavaScript 中 0.1+0.2!== 0.3 ?
在JavaScript 中,数字是以二进制浮点数表示的。
这种表示方式会导致某些十进制小数在二进制下无法精确表示,例如0.1和0.2,
它们在二进制中是无限循环的小数,示例如下:
0.1的二进制表示约为:0.0001100110011001100110011001100110011001100110011001101..:
0.2的二进制表示约为:0.001100110011001100110011001100110011001100110011001101..
所以 0.1+0.2 !==0.3
如果你要深入交流一下这个问题, 请看这篇 : juejin.cn/post/709166…
那么如何让其相等呢?
使用误差范围
一个常见的解决方案是设置一个误差范围,通常称为“机器精度”。在JavaScript中,这个值为
Number.EPSILON,它表示可接受的最小误差范围,示例如下:
function numbersAreEqual(num1, num2){
return Math.abs(num1 -num2)< Number.EPSILoN;
console.log(numbersAreEqual(0.1 + 0.2,0.3));// 输出:true
使用toFixedO方法
将结果四舍五入到指定的小数位数。toFixedO方法会返回一个字符串类型的结果,因此需要注意类型转
换,示例如下:
let sum = 0.1 + 0.2;
let roundedsum=Number(sum.toFixed(1));//注意:toFixed 返回字符串,所以需要转换为数
console.1og(roundedSum===0.3);//输出:true
为什么函数和数组也是对象 ?
函数
- 函数是对象
-
- 从属性角度看
-
-
- 在 JavaScript 中,函数可以拥有属性。例如,可以给一个函数添加自定义的属性,像这样:
-
function myFunction() {
console.log("This is a function");
}
myFunction.myProperty = "This is a property of the function";
console.log(myFunction.myProperty);
- 上述代码中,
myFunction这个函数被添加了一个名为myProperty的属性,并且可以访问和修改这个属性的值。这与对象的行为相似,对象就是由一组属性和方法组成的,函数能够像对象一样拥有属性,这是它被看作对象的一个重要原因。 - 从构造函数角度看
-
- 函数可以作为构造函数来创建对象。例如,使用
new关键字和一个函数来创建对象:
- 函数可以作为构造函数来创建对象。例如,使用
function Person(name, age) {
this.name = name;
this.age = age;
}
var person1 = new Person("John", 30);
console.log(person1.name);
- 在这个例子中,
Person函数被当作构造函数使用。当使用new关键字调用它时,它会创建一个新的对象(person1),并且这个对象的属性(name和age)是由构造函数中的this关键字来定义的。这表明函数在 JavaScript 中具有创建对象的能力,从某种意义上说,它本身的行为也类似于一个对象,能够生成其他对象。 - 从继承角度看
-
- 函数也可以继承属性和方法。JavaScript 中的函数是从
Function.prototype继承而来的。例如:
- 函数也可以继承属性和方法。JavaScript 中的函数是从
function myFunction() {
}
console.log(myFunction.hasOwnProperty('call'));
console.log(myFunction.call === Function.prototype.call);
- 在这里,
myFunction本身没有定义call方法,但是它可以从Function.prototype中继承这个方法。这和对象之间的继承关系类似,进一步说明了函数在 JavaScript 中的对象特性。
数组
- 数组是对象
-
- 从属性和方法角度看
-
-
- 数组有自己的属性和方法。比如,数组有
length属性,可以用来获取数组中元素的个数,像这样:
- 数组有自己的属性和方法。比如,数组有
-
let myArray = [1, 2, 3];
console.log(myArray.length);
- 同时,数组还有许多方法,如
push(用于在数组末尾添加元素)、pop(用于移除数组末尾的元素)、shift(用于移除数组开头的元素)、unshift(用于在数组开头添加元素)等。例如:
let myArray = [1, 2, 3];
myArray.push(4);
console.log(myArray);
- 这些属性和方法的存在使得数组的行为和对象很相似,因为对象也是通过属性和方法来存储和操作数据的。
- 从数据存储方式看
-
- 数组在内存中的存储方式类似于对象。数组中的每个元素就像对象的属性一样,都有对应的索引(类似于对象属性的键),并且可以通过这个索引来访问和修改元素的值。例如:
let myArray = [1, 2, 3];
myArray[0] = 0;
console.log(myArray);
- 这里将数组
myArray的第一个元素(索引为 0)的值从 1 修改为 0,这种通过索引访问和修改元素的方式,和通过键访问和修改对象的属性值是类似的。在底层实现中,数组也是一种特殊的对象,它的索引就相当于对象的键,只是在语法上更加简洁和方便操作,用于存储有序的数据集合。