内存管理机制
内存可以分为三大区域:
代码区:存放程序编译后的机器指令
静态数据区:存放全变变量,静态变量,字符串常量(还可以细分,待了解)
-
动态数据区:理解比较粗糙
- 栈:存放局部变量
- 堆:存放具体的数据值
在速度上,寄存器>栈>堆。
程序运行的流程是怎样的(内存,CPU)?
前言
学习JavaScript的目的是为了爬虫逆向,所以本篇文章只会记录一些JavaScript基础语法(笔者不会的),逆向过程中常遇到的,或者笔者在学习过程中感到新奇的(因人而异)。由于笔者目前只熟悉python,在学习记录过程中,习惯会去对比两者的区别。
新鲜词汇
表达式:指一个为了得到返回值的计算式,凡是 JavaScript 语言中预期为值的地方,都可以使用表达式。
标识符:identifier 指的是用来识别各种值的合法名称,就是变量名或函数名
-
提升:提升作用于 变量声明和函数声明,通过提升,将声明移动到
当前作用域顶端
的默认行为,因此,JavaScript函数能在被声明之前调用。console.log(a); var a = 123;
上面代码中,由于变量提升,并不会报错,此时a的值为undefined。
注意:提升只进行声明操作,并没有初始化。(只有使用var定义的变量【作用域?】,function定义的函数才会被提升) 值传递和引用传递
python是引用传递,java是值传递逗号表达式:在赋值或者函数返回中可见,返回最后一个表达式的计算结果,在js脚本中使用,增加阅读难度。
区块:使用大括号,将多个相关的语句组合在一起,称为“区块”(block)。
对于var命令来说,JavaScript 的区块不构成单独的作用域(scope)。原生函数 function (){[native code]}:JavaScript 引擎提供的原生函数
eval:eval没有自己的作用域,都在当前作用域内执行。如果使用严格模式,eval内部声明的变量,不会影响到外部作用域。
eval的别名调用,作用域都是全局作用域原始类型值:通过调用valueOf,接着调用toString
数据类型
Js中数据类型分为两种,基本类型和引用类型。String、Number、null、undefined、boolean都是基本类型,其他都是引用类型。对比python中的可变类型和不可变类型。
可以使用typeof,instanceof,Object.prototype.toString,可以确定一个值到底是什么类型。
-
String
- btoa():任意值转为 Base64 编码
- atob():Base64 编码转为原来的值
- \ 反斜杆,包括\uxxx,\x7a,\265
-
Number
- Number:强制转换,要比parseInt函数严格很多。基本上,只要有一个字符无法转成数值,整个字符串就会被转为NaN。
- parseInt:将字符串转为整数(逐个转换),还可以用于进制转换。
- parseFloat:将字符串转为浮点数
- isNaN:用于判断NaN,NaN不和任何值相等包括它自己
-
null/undefinded
null == undefined ==> true null === undefined ==> false Number(null) ==> 0 Number(undefined) ==> NaN
null == undefined 为true 这两者值是相等的,数据类型不等。
null表示不存在,undefined表示未定义 -
boolean
在Js中,空数组[]、空对象{},都表示为True。
-
Object:
对象所有键都是字符串,如果是数值,会自动转为字符串- Object.keys():查看一个对象本身的所有属性
- delete:删除指定属性,继承的属性不可删除
- hasOwnProperty:判断是否为对象自身属性
- for...in循环:用来遍历一个对象的全部属性,配合hasOwnProperty,可以得到所有对象自身
-
Array:typeof运算符认为数组的类型就是对象,可以使用isinstance判断
- arr.shift:把数组第一个元素删除,并返回该元素的值
- length:length属性是可写的,清空数组的一个有效方法,就是将length属性设为0
- 空位:
map
set
-
function:
- name:函数名称
- length:返回函数预期传入的参数个数,即函数定义之中的参数个数
- toString:返回一个字符串,内容是函数的源码
定义变量的三种方式
作用域:在ES6之前只分为全局作用域和函数作用域,ES6新增了块作用域。
局部:在函数内部定义的变量作用域为局部(只在函数体内存在),当函数执行完成,变量销毁。
全局:在函数外部声明的变量作用域为全局。
块作用域:{},在if语句块内,在循环体内,let定义变量,const定义常量
var:var定义的变量可以修改,不作用于块级作用域(在块内重新定义变量,会修改原来的值),如果不初始化,会输出undefined。
let:let作用于块级作用域,可以在块内重新定义var定义过的变量,而不会改变原来的值,如果不初始化,会输出undefined。
cons:const定义的变量不可以修改(对象属性,数组元素可以修改,但是该变量的指针已经绑定这个对象,不可修改,不可重新绑定其他对象,类似python中的不可变数据类型),必须初始化。作用于块级作用域
函数
1. 定义函数的三种方式
- 函数声明
function funname(param){
return param;
}
使用函数声明定义的函数,不管函数处于js脚本什么位置,在js脚本运行时,该函数会被预先加载,也就是说,可以在函数被声明之前调用函数。
- 函数表达式--变量赋值
const funname = funtion(param){
return param;
}
// 通过函数构造器 来定义函数
var haha = new Function('a','b','return a+b')
- 匿名函数
(function(param){return param}('hello world'))
2. 函数的参数
-
arguments:接收所有参数
由于 JavaScript 允许函数有不定数目的参数,所以需要一种机制,可以在函数体内部读取所有参数。这就是arguments对象的由来。arguments对象包含了函数运行时的所有参数,arguments[0]就是第一个参数,arguments[1]就是第二个参数,以此类推。这个对象只有在函数体内部,才可以使用。
虽然arguments很像数组,但它是一个对象。
- rest:接收除了定义之外的所有参数
function haha(a,b,...rest){
}
haha(1,2,3,4,5);
a = 1;
b = 2;
rest = [3,4,5];
3. 闭包
闭包的最大用处有两个,一个是可以读取函数内部的变量,另一个就是让这些变量始终保持在内存中,即闭包可以使得它诞生环境一直存在。
标准库
- Object
JavaScript 的所有其他对象都继承自Object对象,即那些对象都是Object的实例。
Object本身是一个函数,可以当作工具方法使用,将任意值转为对象
静态方法,定义在object对象本身的属性方法。动态方法,定义在object.prototype对象上的。Object.keys:返回该对象自身的(而不是继承的)所有属性名,只返回可枚举的属性。
Object.getOwnPropertyNames:还可返回不可枚举的属性
Object.getOwnPropertyDescriptor():获取某个属性的描述对象。
Object.defineProperty():通过描述对象,定义某个属性。
Object.defineProperties():通过描述对象,定义多个属性。
Object.create():该方法可以指定原型对象和属性,返回一个新的对象。
Object.getPrototypeOf():获取对象的Prototype对象。
Object.prototype.toString():返回当前对象对应的字符串形式。
Object.prototype.hasOwnProperty():判断某个属性是否为当前对象自身的属性,还是继承自原型对象的属性。
Object.prototype.isPrototypeOf():判断当前对象是否为另一个对象的原型。
运算符
-
位运算符号
- &: 按位进行与运算,两位同时为1才为1
0101 & 0001 ==》 0001 - |: 或运算,只要有一位为1就为1
0101 | 0001 ==》0101 - ^: 异或运算,相同为0不同为1
0101 ^ 0001 ==》0100 - ~:按位取反
~0101 ==》 1010
- &: 按位进行与运算,两位同时为1才为1
-
逻辑运算符
|| 逻辑或:在条件判断中,两个只要有一个为true,结果就为true
在return返回中,如果左边为true,返回左边结果,否则返回右边结果&& 逻辑与:在条件判断中,两个都为true,结果才为true
在return返回中,如果左边为true,返回右边结果,否则返回左边结果?:三元运算符
一个例子:var votable = (age<18) ? "太年轻":"足够成熟";
先判断左边表达式,如果为true,返回“太年轻”,否则返回‘足够成熟’
-
== 和 ===
- ==:相等运算符,在比较的时,如果类型不同,会发生类型转换,再比较数值
- ===:严格相等运算符,不发生类型转换,如果类型不同,直接为False
-
算术运算符
加法运算符存在重载,其它运算符所有运算子一律转为数值,再进行相应的数学运算。'3' + 4 + 5 // "345" 3 + 4 + '5' // "75"
取反运算符!
用于将布尔值变为相反值,对于非布尔值,取反运算符会将其转为布尔值。如果!!相当于对该对象进行布尔值转换。
面向对象编程
面向对象编程,在python中有类的概念,类是对象的模板,对象是类的实例(不要局限于class
这个字眼)。但是,在JavaScript 语言的对象体系,不是基于“类”的,而是基于构造函数(constructor)和原型链(prototype)。在ES6中新增class。
构造函数
JavaScript 语言使用构造函数(constructor)作为对象的模板。所谓”构造函数”,就是专门用来生成实例对象的函数。它就是对象的模板,描述实例对象的基本结构。一个构造函数,可以生成多个实例对象,这些实例对象都有相同的结构。为了与普通函数区别,构造函数名字的第一个字母通常大写。
构造函数的有两个特点:
- 函数体内部使用了this关键字,代表了所要生成的对象实例。
- 生成对象的时候,必须使用new命令。
关于new命令:
使用new命令运行构造函数,返回一个新对象(必须)。执行流程:
创建一个空对象,作为将要返回的对象实例。
将这个空对象的原型,指向构造函数的prototype属性。
将这个空对象赋值给函数内部的this关键字。
开始执行构造函数内部的代码。
构造函数的缺点
function Dog(name,age){
this.name = name;
this.age = age;
this.say = function(){
console.log('wawawa')
}
}
var dog1 = new Dog('哈士奇',1.5)
var dog2 = new Dog('金毛',2.4)
console.log(dog1.say == dog2.say)
上面的例子中,结果为什么为false?
在使用构造函数创建对象时,每创建一个对象都会开辟一块新的内存空间,也就是每个dog对象都有一个say方法,随着对象的增多,造成内存的浪费也就更多。上面这些不重要,重点是下面,理解prototype
原型prototype
prototype
:原型。每个构造函数的创建出来的时候,系统都会自动的给这个构造函数创建并且关联一个空的对象,这个空的对象,就叫原型。
关键点
每一个构造函数创建出来的对象,都会默认和构造函数的原型对象关联。
当调用对象的方法或者属性时,会在当前对象内查找,如果没有找到,就会去它关联的原型对象查找
也就是说,在原型对象中创建的属性和方法,会被这个构造函数创建出来的对象所共享
访问原型方式 -------- 构造函数名.prototype
function Dog(name,age){
this.name = name;
this.age = age;
}
Dog.prototype.say = function(){
console.log('wawawa')
}
var dog1 = new Dog('哈士奇',1.5)
var dog2 = new Dog('金毛',2.4)
console.log(dog1.say == dog2.say)
一般情况下,不会把属性放在原型中,只会把方法放在原型中。
__proto__
通过构造函数创建出来的对象可用通过 proto 访问原型
dog1.__proto__ === Dog.prototype
constructor属性
constructor
:构造函数,原型的constructor属性指向和原型关联的构造器函数。
每个对象都有这个属性,可以通过``obj.constructor.name`获取该对象的数据类型(创建它的构造器函数名称,父类名称)
dog1.constructor.prototype == dog1.__proto__
每一个对象都有constructor属性,有proto属性,prototype只属于构造器函数
继承
- object.create()
ES6的proxy
proxy
:在爬虫中使用,达到监控的目的,对象属性的获取和设置,补充环境
获取CSSStyleDeclaration对象
在逆向头条的时候,提示 cant not set fontsize of undefined,调试的时候发现,这里的undefined就是一个CSSStyleDeclaration对象,首先是createElement一个span标签,使用innerHTML插入内容,使用element.style创建一个CSSStyleDeclaration对象,给这个对象fontsize设置值。
//方法1:
let CSSStyleDeclaration = dom.style
//方法2:
let CSSStyleDeclaration = getComputedStyle(element, [pseudoElt])
//方法3:
let CSSStyleDeclaration = CSSStyleRule.style
Ajax
浏览器中所有请求都基于XmlRequest对象
async JavaScript and xml
onreadystatechange
ajaxhook
everythinghook
常见语法和函数
a++ 和 ++a
a++ 先执行表达式,在进行自增
++a 先自增,在执行表达式setInteval 和 setTimeout
setInteval:指定周期(毫秒)来调用函数或计算表达式
setTimeout:用于在指定的毫秒数后调用函数或计算表达式。String.fromCharCode(97) = 'a'
charCodeAt('\',0) 返回指定位置字符的 Unicode 编码。这个返回值是 0 - 65535 之间的整数。
参考文章
阮一峰es6
JavaScript进阶
ES6系列之proxy