JavaScript基础知识点总结

383 阅读16分钟

发展史

五大主流浏览器及其内核

  • 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
}