js笔记(阮一峰JavaScript)

js笔记(阮一峰JavaScript)

复习一下js es5部分

后面预计看 你不知道的js & 红宝书 然后补充一部分吧

分了两类,坑和细节,坑是我认为不合理的部分,而细节则是相对合理的但是需要记忆的部分

提升

变量提升

eg1

console.log(a);//undefined 
//要注意这里既然是undefined,至少说明在执行这句之前声明过了a,也就是提升
//否则应该报错 a is not defined
var a = 1;

eg2

function命令声明函数时,整个函数会像变量声明一样,被提升到代码头部。所以,下面的代码不会报错。

f();
function f() {
  console.log("成功调用f");
}
console.log(f);

//成功调用f
//[Function:f]

采用赋值语法,那就报错

f();
var f = function () {
 console.log("成功调用f");
};

console.log(f);

//TypeError: f is not a function
//f现在只是undefined

//等价于
var f;
f();
f = function () {};
函数提升
var f = function () {
  console.log('1');
 }

 function f() {
  console.log('2');
 }

 f()

想想调用f输出的是什么呢?

答案是1,这就是函数提升

js执行起来实际是这样的:

var f;
function f() {
  console.log("2");
}

f = function () {
  console.log("1");
};

就算在函数的重复声明中,也有函数提升现象,具体体现为前一次的声明总是无效的

function f() {
  console.log(1);
 }

 f() // 2

 function f() {
  console.log(2);
 }

 f() // 2

还不是很理解的话参考这篇博客吧

http://www.noobyard.com/article/p-zboayved-dx.html

奇怪的注释

x = 1; <!-- x = 2;

--> x = 3;

-->只有在行首,才会被当成单行注释,否则会当作正常的运算

进制 & parseInt

需要注意的是现在使用前导零的写法已经会报错辣,所以这里如果觉得不考的话其实不重要

进制

前导0表示八进制,处理时很容易造成混乱。ES5 的严格模式和 ES6,已经废除了这种表示法(好似),但是浏览器为了兼容以前的代码,目前还继续支持这种表示法。(确实能跑)

parseInt
坑点

(如果字符串以0x0X开头,parseInt会将其按照十六进制数解析。)

如果字符串以0开头,将其按照10进制解析。是的,不是八进制

如果传一个以0开头的数字,会先把该数字转为字符串再解析,而该转化过程是八进制

详情见下:

第二个参数

2~36之间是预期接收的参数

但是:

//超出范围的算错
parseInt('10', 37) // NaN
parseInt('10', 1) // NaN

//0算没传,为默认的10进制
parseInt('10', 0) // 10
parseInt('10', null) // 10
parseInt('10', undefined) // 10
这会导致一些令人意外的结果

前置知识:合理

如果字符串包含对于指定进制无意义的字符,则从最高位开始,只返回可以转换的数值。如果最高位无法转换,则直接返回NaN

parseInt('1546', 2) // 1
parseInt('546', 2) // NaN

如果parseInt的第一个参数不是字符串,会被先转为字符串。这会导致一些令人意外的结果。

parseInt(0x11, 36) // 43
parseInt(0x11, 2) // 1

// 等同于
parseInt(String(0x11), 36)
parseInt(String(0x11), 2)

// 等同于
parseInt('17', 36)
parseInt('17', 2)

这种处理方式,对于八进制的前缀0,尤其需要注意。

parseInt(011, 2) // NaN

// 等同于
parseInt(String(011), 2)

//String这里是用8进制把11转成了9
// 等同于
parseInt(String(9), 2)

对于那些会自动转为科学计数法的数字,parseInt会将科学计数法的表示方法视为字符串,因此导致一些奇怪的结果。

parseInt(1000000000000000000000.5) // 1

// 等同于
parseInt('1e+21') // 1
parseInt(0.0000008) // 8

// 等同于
parseInt('8e-7') // 8

合理的一些细节:

正常遇到小数会(先转成string)返回小数点前的数字

遇到±也是认识的,正数转了之后不带+号

isNaN

不用它,用NaN不等于自身的特性判NaN会更好

function myIsNaN(value) {
  return value !== value;
 }
 

isNaN的坑

对不是数值的值,会先调Number(),然后再调isNaN(),导致一些预期之外的结果

isNaN('Hello') // true

// 相当于
isNaN(Number('Hello')) // true

//因此
isNaN({}) // true
isNaN(['xzy']) // true

//不过 由于这些Number()可以转成数字,这里就为false
isNaN([]) // false
isNaN([123]) // false
isNaN(['123']) // false

with

with区块没有改变作用域引出的坑

var obj = {};

with (obj) {
 p1 = 4;//没有对p1赋值,而是创建了全局变量p1
 p2 = 5;
}

obj.p1 // undefined
p1 // 4

建议不使用with,使用临时变量代替with的功能

eval

不用 / 少用eval

eval的问题

安全性问题:你的字符串可能会被注入其他的命令或者第三方脚本。
可调试问题:很难去调试错误信息。
压缩问题:程序不会对字符串进行最小化压缩。

通常情况下,eval最常见的场合是解析 JSON 数据的字符串,不过正确的做法应该是使用原生的JSON.parse方法。

var str = '{"name":"sayoriqwq"}';//待解析字符串
// eval写法
// eval("(" + str + ")");

//Function替代
function myEval(fn){
  var Fn = Function;//让Fn指向函数
  return new Fn('return ' + fn)
}
//myEval("(" + str + ")")

//正解
JSON.parse(str)

console.log(str);//{"name":"sayoriqwq"}

使用Function代替eval:(可以看完eval的用法之后对比这个代替的办法的优点)

执行语句:

var code = 'console.log("Hello")';
Function(`"use strict";${code}`)();
//hello

console.log(Function('return 1+2*3')());//7

声明变量(无论是否严格,都只在Function域生效)

var code = 'var a = 123;console.log(a)';
Function(`"use strict"; ${code}`)();
// console.log(a);这里不加注释的话,会报错 a is not defined,证明我们通过code创建的变量a并没有影响到外部作用域

// 123 最后会输出123,内部的语句能正常输出a的值

修改外部变量(无论是否严格,都无法影响到外部变量):

var b = 1234
var code = 'b = 1';
Function(`"use strict"; ${code}`)();
//Function里等号处报错,b is not defined 

关闭严格模式:

var b = 1234
var code = 'b = 1;console.log(b)';
Function(`${code}`)();
console.log(b);
//1 内部使用了在Function作用域的b变量,相当于省略了var语法而已
//1234 但是外界的b并未受到影响

这才是一个安全的方法

基础

eval命令接受一个字符串作为参数,并将这个字符串当作语句执行。

但是它没有自己的作用域,因此可能会修改当前作用域的变量的值,造成安全问题。

JavaScript 规定,如果使用严格模式,eval内部声明的变量,不会影响到外部作用域。

(function f() {
  'use strict';
  eval('var foo = 123');
  console.log(foo);  // ReferenceError: foo is not defined 
  //如果不使用严格模式,则可以正常输出123
 })()
 

但是,即使在严格模式下,eval依然可以读写当前作用域的变量,甚至改写外部变量

var qwq = 1234;
(function f() {
  "use strict";
  var foo = 1;
  eval("foo = 2;qwq = 1");
  console.log(foo); // 2
})();

console.log(qwq);//1
别名调用
var m = eval;
m('var x = 1');
x // 1

上面代码中,变量m是eval的别名。静态代码分析阶段,引擎分辨不出m('var x = 1')执行的是eval命令。

为了保证eval的别名不影响代码优化,JavaScript 的标准规定,凡是使用别名执行evaleval内部一律是全局作用域。

var a = 1;

function f() {
 var a = 2;
 var e = eval;
 e('console.log(a)');
}

f() // 1

上面代码中,eval是别名调用,所以即使它是在函数中,它的作用域还是全局作用域,因此输出的a为全局变量。这样的话,引擎就能确认e()不会对当前的函数作用域产生影响,优化的时候就可以把这一行排除掉。

eval的别名调用的形式五花八门,只要不是直接调用,都属于别名调用,因为引擎只能分辨eval()这一种形式是直接调用。

eval.call(null, '...')

window.eval('...')

(1, eval)('...')

(eval, eval)('...')

上面这些形式都是eval的别名调用,作用域都是全局作用域。

细节

typeof & instanceof

typeof不能判断null,object,array,能识别函数

console.log(typeof [1,2])//object
console.log(typeof null)//object

类型转换

空数组([])和空对象({})对应的布尔值,都是true

标识符

合法:

arg0
_tmp
$elem
π
不合法:
1a  // 第一个字符不能是数字
23  // 同上
***  // 标识符不能包含星号
a+b  // 标识符不能包含加号
-d  // 标识符不能包含减号或连词线

在对象中不符合标识符的键名也是可以取的,不过需要加上'',取值的时候使用[]

标签

top:

 for (var i = 0; i < 3; i++){

  for (var j = 0; j < 3; j++){

   if (i === 1 && j === 1) continue top;

   console.log('i=' + i + ', j=' + j);

  }

 }

// i=0, j=0
// i=0, j=1
// i=0, j=2
// i=1, j=0

// 如果不加标签,单continue会多出这一句
// i=1, j=2

// i=2, j=0
// i=2, j=1
// i=2, j=2

undefined & null & NaN

转为数值时
null:空对象 => 0

undefined:此处无定义 => NaN

NaN:表示非数字,但是类型为number
转为布尔值时

都是false

  • undefined
  • null
  • false
  • 0
  • NaN
  • ""''(空字符串)
返回undefined的经典情况
// 变量声明了,但没有赋值
var i;

i // undefined

// 调用函数时,应该提供的参数没有提供,该参数等于 undefined

function f(x) {

 return x;

}

f() // undefined

// 对象没有赋值的属性

var  o = new Object();

o.p // undefined

// 函数没有返回值时,默认返回 undefined

function f() {}

f() // undefined
NaN
//NaN为数字类型
console.log(typeof NaN);//number
//NaN不等于任何值
NaN === NaN // false
Infinity

溢出是无穷,未定式/错误的运算 是NaN

其他没有列举的计算也都是符合常识的

Infinity - Infinity // NaN
Infinity / Infinity // NaN
0 * Infinity // NaN
0 / 0 //NaN

null作运算时,会转为0

Infinityundefined计算,返回的都是NaN

±0

唯一的区别:

(1 / +0) === (1 / -0) // false (+Infinity !== -Infinity)

精度

-2^53  ~ 2^53
即:
JavaScript 对15位的十进制数都可以精确处理

parseFloat & Number

parseFloat会将空字符串转为NaN
parseFloat(true)  // NaN
Number(true) // 1

parseFloat(null) // NaN
Number(null) // 0

parseFloat('') // NaN
Number('') // 0

parseFloat('123.45#') // 123.45
Number('123.45#') // NaN

string

  1. 字符串内部的单个字符无法改变和增删,这些操作会默默地失败。

  2. javascript对UTF-16的支持不完整,因此,JavaScript 返回的字符串长度可能是不正确的

    例如:对于四字节字符𝌆,浏览器会正确识别这是一个字符,但是 JavaScript 无法识别,会认为这是两个字符。

console.log('𝌆'.length)//2

object

表达式还是语句?

{}里面的一律解释为代码块

如果要解释为对象,最好在大括号前加上圆括号。因为圆括号的里面,只能是表达式,所以确保大括号只能解释为对象。

eval('{foo: 123}') // 123

eval('({foo: 123})') // {foo: 123}
delete
  1. 删除一个不存在的属性,delete不报错,而且返回true(所以不能用delete返回true确定该属性存在)

  2. delete命令只能删除对象本身的属性,无法删除继承的属性

    对于无法删除的属性,虽然delete命令返回true,但该属性并没有被删除

    var obj = {};
    delete obj.toString // true
    obj.toString // function toString() { [native code] }
    

function & 作用域 & 闭包

name

功能:用于获取参数函数的名字

var f3 = function myName() {};
f3.name // 'myName'

//当然真正的函数名还是f3
//myName这个名字只在函数体内部可用(作用域)
作用域

注意函数作为参数传的时候的作用域问题

不过也仅限考题了,还是多用let比较好

var a = 1;
var x = function () {
 console.log(a);
};

function f() {
 var a = 2;//在开发时会提示,已声明a,却从未读取a的值
 //debug方案:1、去掉var,就改全局变量a  2、把这个a作为参数传给x,让x处理参数
 x();
}

f() // 1
参数
  1. 如果一定要省略靠前的参数,只有显式传入undefined

  2. 同名取最后值

  3. 值传递和引用传递(地址)

    在函数内部修改obj的属性p,会影响到原始值。

    在函数内部修改的,不是参数对象的某个属性,而是替换掉整个参数,这时不会影响到原始值。(重新对地址赋值,自然不会影响到保存在原地址上的值)

  4. f.length是预期参数,arguments.length是实际调用传了多少参数

arguments
  1. 伪数组

    转化:

    //silce
    Array.prototype.slice.call(arguments)
    //for逐一push 略
    
  2. 严格模式下,arguments对象与函数参数不具有联动关系。也就是说,修改arguments对象不会影响到实际的函数参数。

var f = function(a, b) {
  'use strict'; // 开启严格模式
  arguments[0] = 3;
  arguments[1] = 2;
  return a + b;
 }

console.log(f(1,1));//严格:2 正常:5
  1. arguments.callee,达到调用函数自身的目的。这个属性在严格模式里面是禁用的,因此不建议使用。
闭包

经典问题,不再赘述

闭包是指有权访问另一个函数作用域中变量的函数

闭包中的变量存储的位置是堆内存

这篇博客写的很清楚

https://juejin.cn/post/6937469222251560990?searchId=20230721172710B2C7563449355BDAAECF

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

sayoriqwq

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值