重学前端笔记(1)

本文详细解释了JavaScript中的Number(),parseInt(),parseFloat()函数的行为差异,以及字符串到数字和数字到字符串的转换过程。讨论了对象的装箱和拆箱转换,以及JavaScript对象的特性、属性和动态性。还涉及了JavaScript类型的判断、变量声明方式、闭包、垃圾回收和前端性能优化等内容。
摘要由CSDN通过智能技术生成

Number(‘30’) // 30

Number(‘0b111’) // 7

Number(‘0o13’) // 11

Number(‘0xFF’) // 255

Number(‘1e3’) // 1000

Number(‘-1e-2’) // -0.01

parseInt 和 parseFloat 并不使用这个转换,所以支持的语法跟这里不尽相同。在不传入第二个参数的情况下,parseInt 只支持 16 进制前缀“0x”,而且会忽略非数字字符,也不支持科学计数法。在一些古老的浏览器环境中,parseInt 还支持 0 开头的数字作为 8 进制前缀,这是很多错误的来源。所以在任何环境下,都建议传入 parseInt 的第二个参数,而 parseFloat 则直接把原字符串作为十进制来解析,它不会引入任何的其他进制。多数情况下,Number 是比 parseInt 和 parseFloat 更好的选择。

要实现 StringToNumber,我们就要根据不同的 Number 输入进行识别,最终统一转化为十进制。

NumberToString

NumberToString 相比于 StringToNumber 会简单一些,类似于实现 toString(raidx)方法。这里主要需要了解的知识点是:如何把十进制的值二进制/八进制/十六进制。这里需要一些进制转换的规则:

.整数十进制转化为 N 进制时,除 N 取余,当余数<N 时,最终结果逆序排列即可

.小数十进制转化为 N 进制时,乘 N 取整,当乘积为整数时,最终结果正序排列即可(可能存在无限循环的情况,所以最好设定一个最大宽度)

详解js中Number()、parseInt()和parseFloat()的区别

Number()

如果是Boolean值,true和false值将分别被转换为1和0。

如果是数字值,只是简单的传入和返回。

如果是null值,返回0。

如果是undefined,返回NaN。

如果是字符串:

a. 如果字符串中只包含数字时,将其转换为十进制数值,忽略前导0

b. 如果字符串中包含有效浮点格式,如“1.1”,将其转换为对应的浮点数字,忽略前导0

c. 如果字符串中包含有效的十六进制格式,如“0xf”,将其转换为相同大小的十进制数值

d. 如果字符串为空,将其转换为0

e. 如果字符串中包含除上述格式之外的字符,则将其转换为NaN

如果是对象,则调用对象的valueOf()方法,然后依照前面的规则转换返回的值。如果转换的结果是NaN,则调用对象的toString()方法,然后再依照前面的规则转换返回的字符串值。

例:

var num1 = Number(“Hello world”);       //NaN

var num2 = Number(“”);            //0

var num3 = Number(“0000011”);        //11

二:parseInt()

处理整数的时候parseInt()更常用。parseInt()函数在转换字符串时,会忽略字符串前面的空格,知道找到第一个非空格字符。

如果第一个字符不是数字或者负号,parseInt() 就会返回NaN,同样的,用parseInt() 转换空字符串也会返回NaN。

如果第一个字符是数字字符,parseInt() 会继续解析第二个字符,直到解析完所有后续字符串或者遇到了一个非数字字符。

parseInt()方法还有基模式,可以把二进制、八进制、十六进制或其他任何进制的字符串转换成整数。

基是由parseInt()方法的第二个参数指定的,所以要解析十六进制的值,当然,对二进制、八进制,甚至十进制(默认模式),都可以这样调用parseInt()方法。

例:

var num1 = parseInt(“AF”,16);   //175

var num2 = parseInt(“AF”);    //NaN

var num3 = parseInt(“10”,2);   //2  (按照二进制解析)

var num4 = parseInt(“sdasdad”);  //NaN

三:parseFloat()

与parseInt() 函数类似,parseFloat() 也是从第一个字符(位置0)开始解析每一个字符。也是一直解析到字符串末尾,或者解析到遇见一个无效的浮点数字字符为止。

也就是说,字符串中第一个小数点是有效的,而第二个小数点就是无效的了,它后面的字符串将被忽略。

parseFloat() 只解析十进制,因此它没有第二个参数指定基数的用法

如果字符串中包含的是一个可解析为正数的数(没有小数点,或者小数点后都是零),parseFloat() 会返回整数。

例:

var num1 = parseFloat(“123AF”);      //123

var num2 = parseFloat(“0xA”);       //0

var num3 = parseFloat(“22.5”);       //22.5

var num4 = parseFloat(“22.3.56”);     //22.3

var num5 = parseFloat(“0908.5”);      //908.5

parseInt() 和parseFloat() 的区别在于:

parseFloat() 所解析的字符串中第一个小数点是有效的,而parseInt() 遇到小数点会停止解析,因为小数点并不是有效的数字字符。

parseFloat() 始终会忽略前导的零,十六进制格式的字符串始终会被转换成0,而parseInt() 第二个参数可以设置基数,按照这个基数的进制来转换。

装箱转换

每一种基本类型 Number、String、Boolean、Symbol 在对象中都有对应的类,所谓装箱转换,正是把基本类型转换为对应的对象,它是类型转换中一种相当重要的种类。前文提到,全局的 Symbol 函数无法使用 new 来调用,但我们仍可以利用装箱机制来得到一个 Symbol 对象,我们可以利用一个函数的 call 方法来强迫产生装箱。我们定义一个函数,函数里面只有 return this,然后我们调用函数的 call 方法到一个 Symbol 类型的值上,这样就会产生一个 symbolObject。我们可以用 console.log 看一下这个东西的 type of,它的值是 object,我们使用 symbolObject instanceof 可以看到,它是 Symbol 这个类的实例,我们找它的 constructor 也是等于 Symbol 的,所以我们无论从哪个角度看,它都是 Symbol 装箱过的对象:

var symbolObject = (function(){ return this; }).call(Symbol(“a”));

console.log(typeof symbolObject); //object

console.log(symbolObject instanceof Symbol); //true

console.log(symbolObject.constructor == Symbol); //true

装箱机制会频繁产生临时对象,在一些对性能要求较高的场景下,我们应该尽量避免对基本类型做装箱转换。使用内置的 Object 函数,我们可以在 JavaScript 代码中显式调用装箱能力。

var num1 = parseInt(“AF”,16);   //175

var num2 = parseInt(“AF”);    //NaN

var num3 = parseInt(“10”,2);   //2  (按照二进制解析)

var num4 = parseInt(“sdasdad”);  //NaN

每一类装箱对象皆有私有的 Class 属性,这些属性可以用 Object.prototype.toString 获取:

var symbolObject = Object(Symbol(“a”));

console.log(Object.prototype.toString.call(symbolObject)); //[object Symbol]

在 JavaScript 中,没有任何方法可以更改私有的 Class 属性,因此 Object.prototype.toString 是可以准确识别对象对应的基本类型的方法,它比 instanceof 更加准确。但需要注意的是,call 本身会产生装箱操作,所以需要配合 typeof 来区分基本类型还是对象类型。

拆箱转换

在 JavaScript 标准中,规定了 ToPrimitive 函数,它是对象类型到基本类型的转换(即,拆箱转换)。对象到 String 和 Number 的转换都遵循“先拆箱再转换”的规则。通过拆箱转换,把对象变成基本类型,再从基本类型转换为对应的 String 或者 Number。拆箱转换会尝试调用 valueOf 和 toString 来获得拆箱后的基本类型。如果 valueOf 和 toString 都不存在,或者没有返回基本类型,则会产生类型错误 TypeError。

var o = {

valueOf : () => {console.log(“valueOf”); return {}},

toString : () => {console.log(“toString”); return {}}

}

o * 2

// valueOf

// toString

// TypeError

我们定义了一个对象 o,o 有 valueOf 和 toString 两个方法,这两个方法都返回一个对象,然后我们进行 o_2 这个运算的时候,你会看见先执行了 valueOf,接下来是 toString,最后抛出了一个 TypeError,这就说明了这个拆箱转换失败了。到 String 的拆箱转换会优先调用 toString。我们把刚才的运算从 o_2 换成 String(o),那么你会看到调用顺序就变了。

var o = {

valueOf : () => {console.log(“valueOf”); return {}},

toString : () => {console.log(“toString”); return {}}

}

String(o)

// toString

// valueOf

// TypeError

在 ES6 之后,还允许对象通过显式指定 @@toPrimitive Symbol 来覆盖原有的行为。

var o = {

valueOf : () => {console.log(“valueOf”); return {}},

toString : () => {console.log(“toString”); return {}}

}

o[Symbol.toPrimitive] = () => {console.log(“toPrimitive”); return “hello”}

console.log(o + “”)

// toPrimitive

// hello

JavaScript对象


JavaScript 对象有如下几个特点。

·对象具有唯一标识性:即使完全相同的两个对象,也并非同一个对象。

·对象有状态:对象具有状态,同一对象可能处于不同状态之下。

·对象具有行为:即对象的状态,可能因为它的行为产生变迁。

对象具有唯一标识性。一般而言,各种语言的对象唯一标识性都是用内存地址来体现的, 对象具有唯一标识的内存地址,所以具有唯一的标识。

任何不同的 JavaScript 对象其实是互不相等的,我们可以看下面的代码,o1 和 o2 初看是两个一模一样的对象,但是打印出来的结果却是 false。

var o1 = { a: 1 };

var o2 = { a: 1 };

console.log(o1 == o2); // false

在 JavaScript 中,将状态和行为统一抽象为“属性”,所以 JavaScript 中的行为和状态都能用属性来抽象。

下面这段代码其实就展示了普通属性和函数作为属性的一个例子,其中 o 是对象,d 是一个属性,而函数 f 也是一个属性,尽管写法不太相同,但是对 JavaScript 来说,d 和 f 就是两个普通属性。

var o = {

d: 1,

f() {

console.log(this.d);

}

};

总结一句话来看,在 JavaScript 中,对象的状态和行为其实都被抽象为了属性。

在实现了对象基本特征的基础上, 我认为,JavaScript 中对象独有的特色是:对象具有高度的动态性,这是因为 JavaScript 赋予了使用者在运行时为对象添改状态和行为的能力。

var o = { a: 1 };

o.b = 2;

console.log(o.a, o.b); // 1 2

为了提高抽象能力,JavaScript 的属性被设计成比别的语言更加复杂的形式,它提供了数据属性和访问器属性(getter/setter)两类。

JavaScript 对象的两类属性


对 JavaScript 来说,属性并非只是简单的名称和值,JavaScript 用一组特征(attribute)来描述属性(property)。

先来说第一类属性,数据属性。它比较接近于其它语言的属性概念。数据属性具有四个特征。

value:就是属性的值。

writable:决定属性能否被赋值。

enumerable:决定 for in 能否枚举该属性。

configurable:决定该属性能否被删除或者改变特征值。

在大多数情况下,我们只关心数据属性的值即可。第二类属性是访问器(getter/setter)属性,它也有四个特征。

getter:函数或 undefined,在取属性值时被调用。

setter:函数或 undefined,在设置属性值时被调用。

enumerable:决定 for in 能否枚举该属性。

configurable:决定该属性能否被删除或者改变特征值。

访问器属性使得属性在读和写时执行代码,它允许使用者在写和读属性时,得到完全不同的值,它可以视为一种函数的语法糖。通常用于定义属性的代码会产生数据属性,其中的 writable、enumerable、configurable 都默认为 true。我们可以使用内置函数 getOwnPropertyDescriptor 来查看,如以下代码所示:

var o = { a: 1 };

o.b = 2;

//a和b皆为数据属性

Object.getOwnPropertyDescriptor(o,“a”) // {value: 1, writable: true, enumerable: true, configurable: true}

Object.getOwnPropertyDescriptor(o,“b”) // {value: 2, writable: true, enumerable: true, configurable: true}

使用了两种语法来定义属性,定义完属性后,用 JavaScript 的 API 来查看这个属性,可以发现,这样定义出来的属性都是数据属性,writeable、enumerable、configurable 都是默认值为 true。

如果我们要想改变属性的特征,或者定义访问器属性,我们可以使用Object.defineProperty

var o = { a: 1 };

Object.defineProperty(o, “b”, {value: 2, writable: false, enumerable: false, configurable: true});

//a和b都是数据属性,但特征值变化了

Object.getOwnPropertyDescriptor(o,“a”); // {value: 1, writable: true, enumerable: true, configurable: true}

Object.getOwnPropertyDescriptor(o,“b”); // {value: 2, writable: false, enumerable: false, configurable: true}

o.b = 3;

console.log(o.b); // 2

这里使用了 Object.defineProperty 来定义属性,这样定义属性可以改变属性的 writable 和 enumerable。

同样用 Object.getOwnPropertyDescriptor 来查看,发现确实改变了 writable 和 enumerable 特征。因为 writable 特征为 false,所以我们重新对 b 赋值,b 的值不会发生变化。

在创建对象时,也可以使用 get 和 set 关键字来创建访问器属性,代码如下所示:

var o = { get a() { return 1 } };

console.log(o.a); // 1
访问器属性跟数据属性不同,每次访问属性都会执行 getter 或者 setter 函数。这里我们的 getter 函数返回了 1,所以 o.a 每次都得到 1。这样,我们就理解了,实际上 JavaScript 对象的运行时是一个“属性的集合”,属性以字符串或者 Symbol 为 key,以数据属性特征值或者访问器属性特征值为 value。对象是一个属性的索引结构(索引结构是一类常见的数据结构,我们可以把它理解为一个能够以比较快的速度用 key 来查找 value 的字典)。我们以上面的对象 o 为例,你可以想象一下“a”是 key。{writable:true,value:1,configurable:true,enumerable:true}是 value。我们在前面的类型课程中,已经介绍了 Symbol 类型,能够以 Symbol 为属性名,这是 JavaScript 对象的一个特色。

css

1,盒模型
2,如何实现一个最大的正方形
3,一行水平居中,多行居左
4,水平垂直居中
5,两栏布局,左边固定,右边自适应,左右不重叠
6,如何实现左右等高布局
7,画三角形
8,link @import导入css
9,BFC理解

js

1,判断 js 类型的方式
2,ES5 和 ES6 分别几种方式声明变量
3,闭包的概念?优缺点?
4,浅拷贝和深拷贝
5,数组去重的方法
6,DOM 事件有哪些阶段?谈谈对事件代理的理解
7,js 执行机制、事件循环
8,介绍下 promise.all
9,async 和 await,
10,ES6 的 class 和构造函数的区别
11,transform、translate、transition 分别是什么属性?CSS 中常用的实现动画方式,
12,介绍一下rAF(requestAnimationFrame)
13,javascript 的垃圾回收机制讲一下,
14,对前端性能优化有什么了解?一般都通过那几个方面去优化的?

一个最大的正方形
3,一行水平居中,多行居左
4,水平垂直居中
5,两栏布局,左边固定,右边自适应,左右不重叠
6,如何实现左右等高布局
7,画三角形
8,link @import导入css
9,BFC理解

[外链图片转存中…(img-0SQZ6cUc-1714768532969)]

js

1,判断 js 类型的方式
2,ES5 和 ES6 分别几种方式声明变量
3,闭包的概念?优缺点?
4,浅拷贝和深拷贝
5,数组去重的方法
6,DOM 事件有哪些阶段?谈谈对事件代理的理解
7,js 执行机制、事件循环
8,介绍下 promise.all
9,async 和 await,
10,ES6 的 class 和构造函数的区别
11,transform、translate、transition 分别是什么属性?CSS 中常用的实现动画方式,
12,介绍一下rAF(requestAnimationFrame)
13,javascript 的垃圾回收机制讲一下,
14,对前端性能优化有什么了解?一般都通过那几个方面去优化的?

[外链图片转存中…(img-pu8XKOkl-1714768532971)]

开源分享:【大厂前端面试题解析+核心总结学习笔记+真实项目实战+最新讲解视频】

  • 29
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值