发展史
五大主流浏览器及其内核
- IE(trident)
- chrome(webkit blink)
- safari(webkit)
- firefox (fecko)
- opera (presto)
浏览器的历史和JS诞生
1.1990 蒂姆伯纳斯 李 超文本分享资讯的人
2.1993 美国伊利诺大学 NSCA组织(马克 安德森) MOSIAC浏览器
3.马克安德森和吉姆克拉克归途SGI MOSIAC communucat corporation
编程语言分类
1.编译型
编译过程: 源码 -> 编译器 -> 机器语言 ->可执行的文件,在执行之前已经翻译完了,编译过就可以执行
优点: 运行速度比较快
缺点: 跨平台需要重新编译,跨平台移植不是很好
2.解释型
翻译过程: 源码 -> 解释器 ->解释一行执行一行
优点: 不需要根据不同的平台进行移植
缺点: 运行比较慢
javaScript重点
ECMAScript
单线程:只能同时做一件事
多线程:可以同时做多件事
JS引擎
1.单线程
2.模拟多线程(轮转时间片:短时间内轮转执行多个任务)
过程:
a)任务1 任务2
b)切分任务1 任务2
c)随机排列这些任务片段,组成队列
d)按照队列顺序将任务片段送进JS进程
DOM(document object model)文档对象模型
w3c规范增删改查标签
BOM(brower object model)浏览器对象模型
没有相应的规范,可以写兼容性;事件
JavaScript引用
外部文件
<script type="text/javascript" src="js/index.js">
内部文件
<script type="text/javascript"></script>
javascript变量
变量容器 -> 存储数据后续使用
变量声明
<script type="text/javascript">
var a // 变量声明 向系统申请一个存储空间
a = 3 // 变量赋值var a = 3
// 变量声明并赋值
// 进行两步
1.变量声明
2.变量赋值// = 将等号后的赋值给等号前
var x, y
var x = 1, // 变量之间使用逗号分隔
y = 2
</script>
变量命名规范
1.不能以数字开头
2.能以字母 _ $开头
3不能使用关键字、保留字
4.语义化命名(要有意义),结构化
5.变量名小驼峰,首字母小写,后面每个单词第一个字母大写
javascript的值
动态语言->脚本语言->解释型语言->弱类型语言 静态语言 -> 编译型语言 -> 强类型语言
(1)原始值(基本类型、基本数据类型)5种
Number(数字类型):包括整形和浮点型
<script type="text/javascript">
var x = 1
var a = 3.14
</script>
String(字符串类型):单引号或者双引号之间
<script type="text/javascript">
var a = 'hello world'
</script>
Boolean(布尔类型): true false计算机非真即假 非假即真
<script type="text/javascript">
var a = true
</script>
undefined声明了变量但没有赋值,未被定义的
<script type="text/javascript">
var a = undefined
var a
</script>
null空值 初始化组件 函数 撤销函数 占位
(2) 引用值
object(对象)
array(数组)
function(函数)
date(日期)
RegExp(正则)
(3)栈内存(先进后出)和堆内存
1.原始值存储存在栈内存中,数据是永久保存不可改的
<script type="text/javascript">
var a = 3 // 声明一个a空间 在a空间存储3
var a = b // 声明一个b空间 在a空间中取到值 复制一份到b空间中
a = 1 // 不会修改原来的 会重新命名一个空间a并在空间a中存储1,原来的空间会将名字擦掉,但原空间中的值不会消失
</script>
2.引用值的地址存在栈内存,值存在堆内存
<script type="text/javascript">
var arr1 = [1, 2, 3] // 在栈内存中声明一个空间arr1,在堆内存中存储值,栈内存的空间中存储的是堆内存中的地址
var arr2 = arr1 // 在栈内存中声明一个空间arr2在arr1的空间中取到值(也就是地址),复制一份到arr2中,所以在调用arr1和arr2的时候都是指向同一个地址,值是完全相同的
var arr1 = [1, 2] // 在栈内存中声明一个新空间arr1,把值存在堆内存中,在新的arr1的空间中存储新的地址,这时候调用arr1和arr2分别指向不同的地址所以值也不同
</script>
JavaScript错误
(1)语法错误
所有语句都不会执行SyntaxError
(2)通用错误:中断执行
错误语句之前语句都会执行,之后的不会执行
不同代码块和代码之间的错误是不会互相影响的
JavaScript运算符
<script type="text/javascript">
var a = 1
var b = 2
var c = 3
var d = (a + b) * c// 声明变量d, 变量a的值和变量b的值相加,再与变量c相乘得到的结果
</script>
加号(+)
<script type="text/javascript">
// + 数学运算
var a = 1
var b = 2
var c = a + b
// 字符串拼接
// 任何数据类型加上字符串的值都是字符串
var c = ''
c = 1 + 'str' // '1str'
c = 'str' + 'str' // 'strstr'
c = 'str' + undefined // 'strundefined'
c = 'str' + null // 'strnull'
c = 'str' + NaN // 'strNaN'
// 从左向右进行计算
c = 'str' + 1 + 1 // 'str' + 1 + 1 -> 'str11'
c = 'str' + (1 + 1) // 'str' + (1 + 1) -> 'str2'
c = 1 + 1 + 'str' // 1 + 1 + 'str' -> 2 + 'str' -> '2str'
</script>
除号(/)
<script type="text/javascript">
// /数学运算
var a = 5
var b = 2
var cc = a / b // 2.5
c = 0 / 0 // NaN -> not a Number 非数
// NaN -> 数字类型c = 'a' / 'b' // NaN
c = NaN / NaN // NaN
c = 5 / NaN // NaN
c = 1 / 0 // Infinity -> 数字类型
c = -1 / 0 // Infinity -> 数字类型
</script>
取模、取余(%)
<script type="text/javascript">
c = 2 % 5 // 2取余
c = 0 % 6 // 0取余
</script>
交换值
<script type="text/javascript">
var a = 1
var b = 2
// a,b 值的交换
var c = a
a = b
b = c
// 方式2
a = a + b // a = 1 + 2 -> 3
b = a - b // b = 3 - 2 -> 1
a = a - b // a = 3 - 1 -> 2
</script>
++、 --
<script type="text/javascript">
var a = 1
// 先打印后运算a++
console.log(a++) // 1
var b = 1
// 先运算后打印
console.log(b++) // 2
</script>
>,< ,>= , <= , == , != , === , !==
<script type="text/javasctipt">
var bool = 1 > 2
console.log(a) // false
// number遇到string先将string转换为number再进行比较
var bool = 1 > '2' // false
// string 遇到string将比较字符串的ascll码(相对应的10进制代码)
var bool = 'a' > 'b' // false
// 会从左向右一位一位进行比较ascll码,直到比较出ascll的大小
var bool = '4.5' > '11' // true
// 相等是不看数据类型的
var bool = 1 == '1' // true
// 全等是要看数据类型是否相等的
var bool = 1 === '1' // false
// NaN与包括自己在内,任何东西都不相等
var bool = NaN == NaN // false
</script>
逻辑运算符
<script type="text/javascript">
// 除undefined,null,NaN, '', 0, false以外都为真
// 遇到真就往后走,遇到假或者走到最后就返回当前值
1 && 1 // 返回1 条件为真
0 && 1 // 返回0 条件为假
1 && 0 // 返回0 条件为假
0 && 0 // 返回0 条件为假
// 遇到假就往后走,遇到真或者走到最后就返回当前的值
1 || 1 // 返回1 条件为真
0 || 1 // 返回1 条件为真
1 || 0 // 返回1 条件为真
0 || 0 // 返回0 条件为假
</script>
判断分支
if条件语句
if(条件) { 语句 }
使用环境一般来说需要判断值范围的,或者条件是多个的
1.在条件中 &&并且两边都必须满足条件 ||或者 两边有一边满足条件即可
2.if 条件:互斥性 如果互斥的就一定不可分开写,就要使用else if
3.使用五个if需要判断五次,如果一直使用else if只需要判断一次寻找满足的条件 就行了
switch条件语句
switch(变量) { case 值1: 语句1; case 值2: 语句2; default: 语句3 }
使用环境一般条件是定值的时候,而且有多个的,一般使用switch
注释
行注释 //
块注释 /***/
循环、引用值初识、显示以及隐式类型转换
循环
步骤:
1.声明变量
2.走if语句 条件满足
3.执行语句
4.i++
5.走if语句 条件满足
6.执行语句
7.i++
8.走if语句 条件不满足 循环结束
<script>
// for循环
for(; i < 10; ) {
console.log(i)
i++
}
// while
while(i < 10) {
// 条件永远满足就会死循环
console.log(i)
i++
}
</script>
引用值初识
array object RegExp function date
array
<script>
var arr = [1, 2, 3, 4]
console.log(arr[3]) // 取值 索引从0开始
arr[3] = 1 // 赋值console.log(arr.length)
// 打印长度
for (var i = 0; i < arr.length; i++) {
console.log(arr[i])
}
</script>
object
<script>
var person = {
// 属性名/键名 : 属性值 / 键值
name: 'xubin',
weight: 120,
height: 180,
job: 'web开发工程师'
}
</script>
类型转换
<script>
// typeof()可以打印的数据类型
// number string boolean undefined
// Object 引用类型 object array
console.log(typeof(123)) // number
console.log(typeof('123')) // string
console.log(typeof(true)) // boolean
console.log(typeof({})) // Object
console.log(typeof([])) // Object
console.log(typeof(null)) // Object
// null最早是指定空对象的一个指针 最早开始说成一个引用类型
console.log(typeof(undefined)) // undefined
console.log(typeof(function(){})) // function
console.log(typeof(1 - 1)) // number
console.log(typeof(1 - '1')) // number
console.log(typeof('1' - '1')) // number
console.log(typeof(a)) // undefined
console.log(typeof(typeof(a))) // string
console.log(typeof(NaN)) // number
</script>
显示类型转换
Number()
<script>
var a = '123'
console.log(typeof(Number(a)) + '-' + Number(a)) // number-123
var a1 = 'true'
console.log(typeof(Number(a1)) + '-' + Number(a1)) // number-NaN
var a2 = true
console.log(typeof(Number(a2)) + '-' + Number(a2)) // number-1
var a3 = null
console.log(typeof(Number(a3)) + '-' + Number(a3)) // number-0
var a4 = undefined
console.log(typeof(Number(a4)) + '-' + Number(a4)) // number-NaN
var a5 = 'a'
console.log(typeof(Number(a5)) + '-' + Number(a5)) // number-NaN
var a6 = '1a'
console.log(typepf(Number(a6)) + '-' + Number(a6)) // number-NaN
var a7 = '3.14'
console.log(typeof(Number(a7)) + '-' + Number(a7)) // number-3.14
</script>
parseInt(a, redix)
<script>
// 转换为整型 以redix为基数转换成10进制 redix取(2-36)
var a = '123'
console.log(typeof(parseInt(a)) + '-' + parseInt(a)) // number-123
var a1 = 'true'
console.log(typeof(parseInt(a1)) + '-' + parseInt(a1)) // number-NaN
var a2 = true
console.log(typeof(parseInt(a2)) + '-' + parseInt(a2))
var a3 = 'true'
console.log(typeof(parseInt(a3)) + '-' + parseInt(a3)) // number-NaN
var a4 = 'undefined'
console.log(typeof(parseInt(a4)) + '-' + parseInt(a4)) // number-NaN
var a5 = 'a'
console.log(typeof(parseInt(a5)) + '-' + parseInt(a5)) // number-NaN
var a6 = '1a'
console.log(typeof(parseInt(a6)) + '-' + parseInt(a6)) // number-1
var a7 = '3.14'
console.log(typeof(parseInt(a7)) + '-' + parseInt(a7)) // number-3
console.log(parseInt('123abc')) // 123
console.log(parseInt('abc123')) // NaN
console.log(parseInt('12',3)) // 1*3^1 + 2*1 = 5
console.log(parseInt('a412', 3)) // NaN
</script>
String()
toString(redix)
<script>
// toString(redix)以10进制为基数转换成redix进制 redix取(2-36)
var str = 3.14
console.log(str.toString()) // 3.14
// null和undefined没有toString()方法
</script>
Bealoon()
undefined null 0 '' false NaN除了这些都是true
隐式类型转换
<script>
var a = '123'
a++ // Number(a)
console.log(a) // 124
var a = 'a' + 1 // String(1)
console.log(a) // a1
var a = '3' * 2 // Number(3)
console.log(a) // 6
var a = '1' > 2 // Number(1)
console.log(a) // false
var a = 'a' > 'b' // 比较ascii
console.log(a) // false
var a = 1 == '1'
console.log(a) // true
var a = 1 === '1' // 不进行隐式转换
onsole.log(a) // false
var a = NaN == NaN
console.log(a) // false
var a = NaN === NaN
console.log(a) // false
var a1 = 2 > 1 > 3 // 2 > 1 -> true -> 1 > 3 -> false
console.log(a1) // false
var a2 = 2 > 1 == 1
console.log(a2) // true
// undefined和null既不大于0也不等于0
undefined == null // true
undefined === null // false
</script>
函数基础与种类、形实参及映射、变量类型
函数xy任意一个x的值都会有一个确定的xy自变量,函数值是确定的
函数式编程
function test(a, b, c) {
// 执行语句
}
耦合:重复的代码太多,代码的重复度太高了
高内聚:强功能性,高独立性
低耦合:把重复的代码提取出来 -> 模块的单一责任制
解耦合:使用函数可以很好的进行解耦合
1.最基本的函数写法-> 函数声明 函数只有被调用的时候执行
function text(参数){
函数的多个执行语句
}
2.函数名的命名规则不能以数字开头,字母 _ $包含数字 驼峰命名法 复合单词
3.函数声明的方式
function text(参数) {
函数的多个执行语句
}
匿名函数表达式 函数字面量
var test = function test1(参数) {
函数的多个执行语句
}
4.函数的组成部分
function 函数名 参数(可选) 返回值(可选) return占位符 -> 形参
function test(a, b, c) {
console.log(a, b, c)
}
// 形参和实参数量可不等
// 实际参数 -> 实参 参数没有数据类型
test(1, 2)
function test(a, b) {
console.log(test.length) // 获取形参的长度
console.log(arguments.length) // 获取实参的长度
}
5.函数内部有个arguments储存实参 一个函数被调用时,累加它的实参 循环在arguments中储存的实参
function sum() {
var a = 0
for (var i = 0; i < arguments.length; i++) {
a += arguments[i]
}
console.log(a)
}
sum(1, 2, 3, 4, 5, 6)
在函数内部是可以改变实参
当我们在实参中传了值的可以在函数中进行修改,如果说没有在实参中传递是undefined,是不可进行传递的
实参和形参不是同一个变量,但是存在映射关系,当形参进行改变的时候,实参也会跟着改变,但如果没有实参,就没有映射关系
function test(a, b) {
a = 3
console.log(arguments[0])
}
6.return 每个函数最后一定是return,如果没写,js会隐式的加上,return之后的语句是不会执行的
1.终止函数的执行
2.返回一个东西
3.没有写return默认返回undefined
7.函数就是一个功能段或者程序段,被封装的过程
实现一个固定的功能或者程序,在这个封装体中需要一个入口和一个出口,入口就是参数,出口就是返回
参数默认值、递归、预编译、暗示全局变量
参数默认值
初始化函数 默认值undefined
function test(a, b) {
console.log(a)
console.log(b)
}
test(1)
设置参数默认值
1.方法1: 不兼容低版本的浏览器
function test(a = 1, b) {
console.log(a)
console.log(b)
}
test(undefined, 2)
2.方法2
function test(a, b) {
var a = arguments[0] || 1
var b = arguments[1] || 2
console.log(a + b)
}
test(0 ,6)
3.方法3
function test() {
var a, b
if (typeof (arguments[0] !== 'undefined')) {
a = arguments[0]
} else {
a = 1
}
}
递归
递归,函数自己调用自己
1.找到一个规律n! = n* f(n-1)
2.找到一个出口
预编译过程
通篇的检查语法错误;预编译的过程;解释一行执行一行
test()
function test() {
console.log(1)
}
console.log(a)
var a// 函数声明整体提升,变量只有声明是提升的,赋值是不提升的
console.log(a)
function a(a) {
var a = 10
var a = function() {}
}
var a = 1
函数执行之前进行的步骤
1.创建AO对象 -> AO activation object 活跃对象 函数上下文
2.寻找函数中的形参和变量声明
3.将实参赋值给形参
4.寻找函数体内函数声明 赋值函数体
5.执行
function test(a, b) {
console.log(a) // 1
c = 0
var c
a = 5
b = 6
console.log(b) // 6
function b() {}
function d() {}
console.log(b) // 6
}
test(1, 2)
AO = {
a: undefined -> 1 -> 5
b: undefined -> 2 -> function b() {} -> 6
c: undefined -> 0
d: function d() {}
}
全局上下文
1.创建GO对象 -> 全局上下文 === window
2.寻找变量声明
3.寻找函数声明
4.执行
暗示全局变量
1.暗示全局变量的问题 imply global variable
2.在全局上的时候,无论是var a = 1 或者 a = 1都是放在window对象上的
作用域、作用域链、预编译、闭包基础
作用域
对象
函数也是一种对象类型、引用类型、引用值
function test(a, b) {}
可以访问的属性test.length test.name test.prototype
对象-> 有些属性使我们无法访问的
js引擎内部固有的隐式属性
[[scope]]域
1.函数创建时,生成的一个js内部的隐式属性,只能js引擎读取
2.函数用来存储作用域链的容器,作用域链(AO、GO)
AO函数的执行期上下文
GO全局的执行期上下文
函数执行完成后,AO是被摧毁的
如果再次执行这个函数的时候会重新生成一个全新的AO
AO是一个即时的存储容器
作用域链
function a() {
function b() {
var b = 2
}
var a = 1
b()
}
var c = 3
a()
页面打开的时候全局已经在执行,在执行的前一刻会生成GO -> 函数声明已经定义 全局执行的时候函数表达式才赋值
1.当函数a被定义的时候
系统生成一个[[scope]]属性,[[score]]中保存该函数的作用域链
在作用域链的第0位保存当前环境下的全局执行上下文GO
GO中保存全局下的所有对象,其中包含函数a和全局变量c = 3
2.当函数a被执行时(前一刻,预编译的时候)
系统生成一个[[scope]]属性保存函数的作用域链
作用域链的顶端(第0位)保存a函数生成的函数执行期上下文AO
GO被保存到作用域链的第1位
查找变量是从a函数的作用域从顶端往下依次查找
3.a在执行的同时函数b函数被定义时,
在a函数环境下,b函数在定义的时候和a函数被执行的时候作用域是相同的
4.当函数b函数被执行时(前一刻,预编译的时候)
系统生成一个[[scope]]属性保存该函数的作用域链
第0位保存自身的AO,a函数的AO和全局的GO依次向下排序
5.当函数b函数执行结束的时候
b函数的AO被销毁,回归b被函数定义的时候的[[scope]]
6.当函数a执行结束的时候
函数a的AO被销毁同时b函数的[[scope]]也销毁了
a函数回归a函数被定义的时候
闭包基础
function test1() {
var c = 3
function test2() {
var b = 2
console.log(a)
}
var a = 1
return test2
}
var c = 3
var test3 = test1()
test3()
闭包
当内部函数被返回到外部并保存时,一定会产生闭包
闭包会产生原来的作用域链不释放
过渡的闭包可能会导致内存的泄露,或者加重太慢
立即执行函数、闭包深入、逗号运算符
普通的函数在全局定义之后是存在GO中的,是不释放的,在想调用的时候就能调用,有些函数只需要运行一次之后再也不会执行->立即执行函数
立即执行函数(初始化函数)
IIFE-> immedicately-invoked function expression
自动执行
执行完自己销毁
1.立即执行函数的写法
(function() {
})()
(function(){
}()) // w3c建议
2.立即执行函数可以进行传参
(function(形参){
语句
}(实参))
3.立即执行函数可以返回值,声明一个变量来保存返回的值
var num = (
function(a, b) {
return a + b
}(2, 5)
)
4.一定是表达式才能执行符合()执行 括号包起来的都能变成表达式
当函数声明使用括号转换成表达式的时候,自动忽略函数名
(function test() {
console.log('hello world')
})()
(function(){
console.log('hello world')
}())
函数声明变成表达式的方法+ - ! || && 立即执行函数的函数名自动被忽略
+ function test() {
console.log(1)
}()
! function test() {
console.log(1)
}()
~ function test() {
console.log(1)
}()
0 || function test() {
console.log(2
)}()
逗号运算符
逗号运算符 返回所有逗号最后的一个值
var num = (2 - 1, 6 + 6, 24 + 1)
console.log(num) // 25
闭包高级、对象、构造函数、实例化
对象
添加
// 添加属性
teacher.address = '北京'
// 添加方法
teacher.drink = function() {
console.log('I am drinking beer')
}
查找
// 属性查找
teacher.name
// 方法查找
teacher.teach()
删除
// 删除属性
delete teacher.address
// 删除方法
delete teacher.teach
创建对象方法
1.var obj = new Object()
2.var obj = {}
3.自定义构造函数
构造函数
1.大驼峰与普通函数表面上的区别
2.只有new构造函数之后才会有实例,在没有执行之前就不存在this
3.this指向的是对象
4.通过一个构造函数实例化出来的两个对象之间没有关系,是完全不同的两个对象
构造函数以及实例化原理、包装类
构造函数以及实例化原理
this
this没有执行讨论this没有意义,this在函数执行的时候指向window,没有实例化的时候指向window,当实例化的时候this指向被实例化出来的对象
构造函数实例化过程
(1)当构造函数被实例化的时候,相当于函数被执行了,会产生AO,会自动先保存一个this对象(不是空对象)
(2)new的时候相当于函数跑完了,因为this指向实例,所以在外界可以通过实例进行访问,访问到this中的属性
(3)在this中保存了this相对应的属性,构造函数实例化的时候返回一个引用值,this指向被实例化出来的对象
包装类
原始值(string, number, null, undefined, boolean)没有自己的方法和属性
原型、原型链、闭包立即执行函数
(1)原型prototype
1.原型prototype其实是function对象的一个属性,打印出来的也是对象
2.原型prototype是定义构造函数构造出来的每个对象的公共祖先
3.所有被该构造函数构造出来的对象都可以继承原型prototype上的属性和方法
4.自己this上有的属性和方法是不会往原型上去查找的
5.当需要参数进行传值的时候一般写在this中
6.原型中有个默认construct构造器,指向构造函数本身
7.constructor原型上的construct指向构造函数本身
8.__proto__
__proto__属于每一个实例化对象
每个实例化对象的原型的容器,通过__proto__访问prototype
9.当构造函数被实例化new的时候,产生了this对象,this对象中默认有一个__proto__里面装了原型prototype
原型与原型链深入、对象继承
原型链
1.所有的对象都有原型,包括原型本身
2.沿着__pro__往上去找原型上相应的属性,一层一层的往上去继承原型的属性的链条叫做原型
3.原型链的顶端是Object的原型
Professor.prototype.tSkill = 'JAVA'
function Professor() {}
var professor = new Professor()
// var professor = { __proto__: Professor.prototype }
Teacher.prototype = professor // { __proto__: Professor.prototype }
function Teacher() {
this.mSkill = 'js'
}
var teacher = new Teacher()
// var teacher = {__proto__: {__proto__:Profressor.prototype},mSkill: 'js'}
Student.prototype = teacher
function Student() {
this.skill = 'html'
}
var student = new Student()
4.原型链上的增删改原始值可以修改自己引用值但不推荐
对象继承
1.声明对象
var obj1 = {}
var obj2 = new Object // 不建议
使用自定义构造函数,原型是构造函数的原型,构造器指向自定义的构造函数
Object.create(对象或者null),创建对象自定义原型的功能
2.不是所有的对象都继承于Object.prototype
继承深入、call apply、圣杯模式、模块化
继承
function Teacher() {
this.name = 'Mr.Lin'
this.tSkill = 'JAVA'
}
Teacher.prototype = {
pSkill: 'js'
}
var t = new Teacher()
function Student() {
this.name = 'Mr.Wang'
}
Student.prototype = Teacher.prptotype
// 公共原型
// 这样的继承使得两个原型的指向是同一个地址,一个修改原始值另一也会修改
var s = new Student()
圣杯模式
1.定义一个缓冲构造函数
2.将父级的prototype赋值给缓冲的原型
3.实例化缓冲实例对象
4.将实例化的缓冲对象赋值给子级的prototype
var inherit = (
function test() {
var Buffer = function() {}
return function(Target, Origin) {
Buffer.prototype = Origin.prototype
Target.prototype = new Buffer()
Target.prototype.constructor = Target
Target.prototype.super_class = Origin
}
})()
Teacher.prototype.name = 'Mr.Zhang'
function Teacher() {}
function Student() {}
inherit(Student, Teacher)
call apply
改变this的指向
function Car(brand, color) {
this.brand = brand
this.color = color
}
var newcar = {}
Car.apply(newcar, ['Banz', 'red'])
对象属性遍历、this、caller callee
对象枚举 -> 遍历
var car = {
brand: 'Benz',
color: 'red',
displacement: '3.0',
lang: '3',
width: '5'
}
// for ... in既可以遍历对象也可以遍历数组
for (var key in car) {
console.log(key + ':' + car[key])
}
对象.hasOwnProperty(属性名)排除原型上自定义的属性
function Car() {
this.brand = 'Benz'
this.color = 'red'
this.displacement = '3.0'
}
Car.prototype = {
lang: 5,
width: 2.5
}
Object.prototype.name = 'Object'
var car = new Car()
for (var key in car) {
// 去掉原型上自定义的属性
if (car.hasOwnProperty(key)) {
console.log(key + ':' + car[key])
}
}
三种判断数组的方法
1.console.log(arr.constructor)
2.console.log(arr instanceof Array)
3.console.log(Object.prototype.toString.call(arr))
this的指向
1.全局的this指向window
2.预编译的时候this指向window
3.构造函数实例化的时候this指向被实例化出来的对象
4.call/apply更改this指向
三目运算、对象克隆、浅拷贝、深拷贝
三目运算
格式
条件 ? 满足条件后执行的语句 : 不能满足条件后执行的语句
浅拷贝
浅拷贝就是将对象中的所有元素复制到新的对象中;
浅拷贝对于原始类型与引用类型的方式区别:
原始值类型字段的值被复制到副本中,在副本中的修改不会影响源对象对应的值
引用类型的字段被复制到副本中的是引用类型的引用,而不是引用的对象
在副本中对子元素引用类型的字段值修改后,源对象的值也会被修改
深拷贝
深拷贝也同样是将对象中的所有字段复制到副本对象中
对于子元素的引用类型也是创建并复制
在副本中对子元素引用类型的字段值被修改后,源对象的值不会被修改
function deep(origin, target) {
var toStr = Object.prototype.toString
var arrType = '[object Array]'
target = target || {}
for (var key in origin) {
if (origin.hasOwnProperty(key)) {
if (typeof(origin[key]) === 'object' && origin[key] !== null) {
if (toStr.call(origin[key]) === arrType) {
target[key] = []
} else {
target[key] = {}
}
deep(origin[key], target[key])
} else {
target[key] = origin[key]
}
}
}
return target
}
深拷贝实例、数组基础、数组方法、数组排列
数组
声明数组(三种方式)三种方式构造出来的数组都继承于Array.prototype继承方法和属性
var arr1 = [] // 数组字面量
var arr2 = new Array() // 构造函数声明数组,不推荐var arr3 = Array() // 不推荐
var arr = [, , ,] // 最后一个是空值,会自动截掉最后一个逗号
var arr = [, 2, , 4] // 稀松数组
var arr1 = new Array(, 2, 1, , , 3)// 用内置的构造函数构造的时候不能有空值
var arr2 = new Array(4) // [, , , ,]只传入一个数字的时候就是创建数组的长度
数组的操作
读取
var arr = [1, 2, 3, 4, 5, 6]
console.log(arr[6]) // undefined
console.log(arr[5]) // 6
写入
var arr = [1, 2, 3, 4, 5, 6]
arr[6] = 'a'
修改
var arr = [1, 2, 3, 4, 5, 6]
arr[5] = 'a'
数组方法
修改原数组
push unshift 添加
pop shift 删除
reverse
splice
sort
arr.push(参数)
在数组的最后一位加,可以加多个值
返回值:经过处理以后的数组长度
arr.unshift(参数)
在数组的第一位加,可以加多个值
返回值:经过处理以后的数组长度
pop
剪切掉最后一位的值,没有参数
返回值:返回被删除的值
shift
剪切掉最后一位的值,没有参数
返回值:返回被删除的值
倒序
arr.reverse()
剪切
arr.splice(开始项的下标,剪切的长度, 剪切以后最后一位开始添加的数据)
splice原理
function splice(arr, index) {
return index += index >= 0 ? 0 : arr.length
}
数组排序sort()
sort()返回值: 返回排序之后的结果
sort是按ASCII码进行排列的
//1 参数a,b
//2 返回值
// 负值a就排在前面
// 正值b就排在前面
// 0 保持不变
sort(function(a, b) {})
var arr = [-1, -5, 8, 0, 2]
arr.sort() // 什么都不会传会默认按升序进行排序
var arr = [27, 48, 5, 7]
arr.sort()
console.log(arr) // [27, 48, 5, 7] // sort是按照ASCII码进行排列的会一位一位去按照ASCII码进行排列的
冒泡排序
var arr = [27, 49, 5, 7]
arr.sort(function(a, b) {
if (a < b) {
return 1
} else {
return -1
}
})
随机排序
arr.sort(function(a, b) {
var rand = Math.random()
if (rand - 0.5 > 0) {
return ran
} else {
return -1
}
return Math.random() - 0.5
})
数组方法、类数组
concat
arr1.concat(arr2) 拼接数组将arr2拼接到arr1的末尾
var arr1 = ['a', 'b', 'c']
var arr2 = ['d', 'e', 'f']
var arr3 = arr1.concat(arr2)
var arr4 = arr2.concat(arr1)
console.log(arr3) // ['a', 'b', 'c', 'd', 'e', 'f']
console.log(arr4) // ['d', 'e', 'f', 'a', 'b', 'c']
toString()以字符串的方式进行打印
var arr1 = ['a', 'b', 'c']
console.log(arr1.toString()) // a,b,c
slice截取
slice(开始的下标(包含),结束元素的下标之前)
返回值是被截取的数据
join把每个元素取出去用括号中的参数进行分割,组成一个字符串
var arr1 = ['a', 'b', 'c']
var str1 = arr1.join('-')
console.log(str1) // a-b-c
split(参数1, 参数2)
split把字符串通过参数1进行分割,参数2表示长度 将根据长度进行剪切
var arr1 = ['a', 'b', 'c', 'd']
var str1 = arr1.join('-')
str1.split('') // ['a', '-', 'b', '-', 'c', '-', 'd']
str1.split('', 3) // ['a', '-']
类数组
类似于数组的对象
1.下标类似的属性
2.系统内置生成length属性
3.可以继承Array.prototype.slice将一个对象变成数组
4.需要什么方法就从Array.prototype中去继承
// push重写Array.prototype.push = function(ele) {this[this.length] = elethis.length++}
自定义原型方法、去重、封装typeof
去重
Array.prototype.unique = function() {
var temp = {}
var newarr = []
for (var i = 0; i < this.length; i++) {
if (!temp.hasOwnProperty(this[i])) {
temp[this[i]] = this[i]
newarr.push(this[i])
}
}
return newarr
}
封装typeof
function myTypeof(val) {
var type = typeof(val)
var toStr = Object.prototype.toString
var res = {
'[object Array]' : 'Array',
'[object Object]' : 'Object',
'[object Number]' : 'object number',
'[object String]' : 'object string',
'[object Boolean]' : 'object boolean'
}
if (val === null) {
return 'null'
} else if (type === 'object') {
var ret = toStr.call(val)
return res[ret]
} else {
return type
}
}
变量生命周期、垃圾回放原理
垃圾回收机制
执行环节的时候负责管理代码执行过程中使用的内存(变量,函数)
原理:
1.找出不再使用的变量
2.释放其占用的内存
3.固定的时间间隔运行
变量的生命周期
存在生命周期的变量 函数内部的变量 局部变量 -> 函数执行过程中存在(栈或者堆内存存储)
forEach/filter/map的使用和重写
forEach
forEach(参数1, 参数2) 遍历 针对数据 arr原型上面的方法
参数1: function -> forEach每遍历出一个元素就会执行这个function -> 需要提供三个参数 ele index array
参数2: 改变forEach中this指向的前提是对象
forEach方法重写
// 传入一个回调函数,第二个参数可不传 使用arguments[1]去获取
Array.prototype.myForEach = function(fn) {
// 声明一个变量arr去保存thisvar arr = this
// 声明一个变量len去保存数组长度var len = arr.length
// 如果传入第二个参数args就赋值为传进来的对象,如果没有就默认
windowvar arg = arguments[1] || window
for (var i = 0; i < len; i++) {
// 使用apply改变fn函数的指向,传入了第二个参数就指向传进来的对象 默认指向window,使用数组的方式将fn所需的参数传进去
fn.apply(arg, [arr[i], i, arr])
}
}
filter
filter(参数1,参数2) 筛选 过滤 返回一个新的数组将满足条件的数组放进这个新的数组
filter重写
Array.prototype.myFilter = function(fn) {
var arr = this
var len = arr.length
var arg = arguments[1] || window
var newArr = []
var item
for (var i = 0; i < len; i++) {
item = JSON.parse(JSON.stringify(arr[i]))
fn.apply(arg, [arr[i], i, arr]) ? newArr.push(item) : ''
}
return newArr
}
map(参数1, 参数2)
映射 返回一个数组
map重写
Array.prototype.myMap = function(fn) {
var arr = this
var len = arr.length
var arg = arguments[1] || window
var newArr = []
var item
for (var i = 0; i < len; i++) {
item = JSON.parse(JSON.stringify(arr[i]))
newArr.push(fn.apply(arg, [item, i, arr])
}
return newArr
}
every/some/reduce/reduceRight使用与重写
every判断数组中有没有
1.Array.prototype.every原型上的一个方法
2.如果有一个不满足条件就停止遍历,条件是return后面表达式
3.返回一个布尔值,返回停止遍历的条件的布尔值
every重写
Array.prototype.myEvery = function(fn) {
var arr = this
var len = arr.length
var arg = arguments[1] || window
var res = true
for (var i = 0; i < len; i++) {
if (!fn.apply(arg, [arr[i], i, arr])) {
res = false
break
}
}
return res
}
some
1.Array.prototype.some原型上的一个方法
2.如果有一个满足条件就停止遍历,条件是return后面表达式
3.返回一个布尔值,返回停止遍历的条件的布尔值
some重写
Array.prototype.mySome = function(fn) {
var arr = this
var len = arr.length
var arg = arguments[1] || window
res = false
for (var i = 0; i < len; i++) {
if (fn.apply(arg, [arr[i], i, arr])) {
res = true
break
}
return res
}
reduce归纳函数
格式arr.reduce(function(prev, elem, index, arr){}, initialValue)
reduce重写
Array.prototype.myReduce = function(fn, init) {
var arr = this
var len = arr.length
var arg = arguments[2] || window
var item
for (var i = 0; i < len; i++) {
item = JSON.parse(JSON.stringify(item))
init = fn.apply(arg, [init, item, i, arr])
}
return init
}