1.发展史、ECMA、编程语言、JS相关、变量、JS值
1.1 五大主流浏览器内核
- IE: trident
- Chrome: webkit blink
- safari: webkit
- firefox: gecko
- opera: presto
1.2编程语言
- 编译型 翻译过程: 源码 -> 编译器 -> 机器语言 -> 可执行的文件
- 解释型 翻译过程: 源码 -> 解释器 -> 解释一样执行一行
1.3JavaScript
- ECMAscript: 语法、变量、关键字、保留字、值、原始类型、引用类型运算、对象、继承、函数
- DOM: document object model W3C规范
- BOM: browser object model 没有规范
1.4单线程和多线程
js引擎肯定是单线程的但是可以模拟多线程
轮转时间片:短时间内轮流执行多个任务的片段
- 任务1 任务2
- 切分任务1 任务2
- 随机排列这些任务片段,组成队列
- 按照这个队列顺序将任务片段送进JS进程
- js线程执行一个又一个的任务片段
由于执行的很快,所以给我们的感觉是多线程的
1.5变量
var a; // 1.变量声明
a = 3; // 2.变量赋值
var b = 3; // 3.变量声明并赋值
var c = a + b; //4.运算大于赋值
/*
动态语言 -> 脚本语言 -> 解释型语言 -> 弱类型语言
静态语言 -> 编译型语言 -> 强类型语言
*/
1.6基本数据类型(原始值)
原始值(基本数据类型)
- Number:数字
- String:字符串
- Boolean:布尔值(true/false)
- undefined:未被定义的
- null:空值
1.7引用数据类型(引用值)
- Object:对象
- arry:数组
- function:函数
- date:日期对象
- RegExp:正则表达式
1.8 堆内存和栈内存
原始值: 都存在栈内存中 先进后出 数据永久保存不可更改的
2.基本语法、规范、错误、运算符、判断分支、注释
2.1交换值的问题
var a = 1,
b = 2;
// 1.
var c = a;
a = b;
b = c;
// 2.
a = a +b;
b = a - b;
a = a - b;
2.2比较运算符
比较运算符有隐式类型转换, 字符串比较会取对应的ASCII码值进行比较(多个字符从左到右依次对比,直到比较出 ASCII码的大小为止), 小数比较也会取ASCII码从第一位开始比较
NaN与任何东西都不相等包含它自己
2.3逻辑运算符
undefined NaN null 0 ‘’ false除开这些都是真
- 与 &&:遇到真就往后走返,遇到假或者走到最后了就返回当前值
- 或 ||:遇到假就往后走,遇到真或者走到最后了就返回当前的值
- 非 !
3.循环、引用值初识、显示及隐式类型转换
3.1循环
//1.for循环
for(var i = 0; i < 10; i++) {
console.log(i)
}
for(初始胡变量;条件表达式;操作表达式) {
循环体
}
// 双重for循环,外层循环执行一次,里面循环执行全部
for(外层初始胡变量;外层条件表达式;外层操作表达式) {
循环体1
for(里层初始胡变量;里层条件表达式;里层操作表达式) {
745874循环体2
}
}
/*
步骤:
1.声明变量i = 0
2.if(i < 10) {
log(i)
}
3. i++
4.i = 1了
5.if(i < 10) { 不满足条件时停止循环
log(i)
}
6. i++
*/
// break 退出循环
// continue 结束本次循环,直接继续执行下次循环
// 2.while循环
while(条件表达式) {
循环体
}
var a = 1;
while(a < 10) {
num++
}
// do while循环
do{
循环体
} while(条件表达式)
// dowhile至少会执行一次循环体,因为先do做后while判断
用来计数跟次数相关的用for while和dowhile用来做更复杂的
3.2 typeof()
console.log(typeof(typeof(a)))–string因为typeof返回值是字符串格式
可以打印出 number string boolean object undefined function
3.3显示类型转换
- Number()强制转换
- parseInt()转换成整型,必须以数字开头否则返回NaN,不四舍五入
- parseFloat()转为数字,可转换小数,如果无法转换则返回NaN
- toFixed()保留几位小数,会四舍五入
- String()转字符串
- toString()转字符串–undefined和null无toString方法
- Boolean()代表空、否定的值会被转成false 例如0,NaN,Null,’’,undefined其余的全是true
3.4隐式类型转换
- ++, --, *, /, %, -, >, <, >=, <=, ==, !=
- isNaN()检查其参数是否是非数字值-也会先进行隐式转换
4.函数基础、函数种类、形实参及映射、变量类型
4.1函数
编程最基本原则:高内聚()、低耦合(重复代码提取出来,成为一个独立的模块)
// 最基本的函数写法 - 函数声明式
function test('形参') {
// 函数执行语句
}
// 匿名函数表达式 函数字面量
var fn = function ('形参') {
// 函数执行语句
}
// 注:函数都需要return 不写的话js会默认在最后添加一个return语句来终止函数
function test(a, b) {
a = 3;//函数内部可以更改实参的值(实参没传值的不可修改)
b = 4;
console.log(arguments[0])
}
text(1)
4.2案例
// 1.n的阶乘(不能循环) -> 递归
function fact(n) {
if(n === 1) return 1;
return n * fact(n - 1)
}
//2.斐波那契数列(不能循环)
function fb(n) {
if(n <= 2) return 1;
return fb(n -1) + fb(n - 2)
}
5.函数参数默认值、递归、预编译流程、原理、现象、暗示全局变量
5.1初始化参数
function test(a, b) {
// es6之前的常用兼容写法
var a = arguments[0] || 1;
var b = arguments[1] || 2;
}
test()
5.2递归
一个函数内部调用自己就是递归,记住递归一定要有结束条件要不就是死循环了
function fn(x) {
if(x > 0) {
return x + fn(x-1)
} else {
return 0
}
}
fn(3)
/* 解析
fn(3) = 3 + fn(3-1)
= 3 + 2 + f(2-1)
= 3 + 2 + 1 + f(1-1)
*/
5.3预编译
- 检查通篇的语法错误
- 预编译的过程
- 解释一行执行一行
函数声明整体提示, 变量只有声明提升
5.4函数执行上下文(Activation Object)
- 创建AO对象(Activation Object)函数上下文
- 找形参和变量声明,将形参名和变量作为AO属性名,值为undefined
- 将实参值赋值给形参
- 在函数体里面找函数声明, 值赋予函数体
- 执行
function test(a) {
console.log(a);
var a = 1;
console.log(a);
function a() {}
console.log(a);
var b = function (){}
console.log(b);
function d(){}
}
test(2)
/*
函数执行上下文
AO = {
a: undefined -> 2 -> function a() {}
b: undefined
d: function d() {}
}
执行
AO = {
a: undefined -> 2 -> function a() {} -> 1
b: undefined -> function (){}
d: function d() {}
}
*/
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)
/* 函数执行上下文
AO = {
a: undefined -> 1
b: undefined -> function b() {}
c: undefined
d: function d() {}
}
执行
AO = {
a: undefined -> 1 -> 5
b: undefined -> function b() {} -> 6
c: undefined -> 0
d: function d() {}
}
*/
5.5全局执行上下文(global Object)
- 创建GO对象(global object)全局上下文
- 找变量声明,将变量名作为GO属性名,值为undefined
- 找函数声明,作为GO属性,值赋予函数体
- 执行
var a = 1;
function a() {
console.log(2);
}
console.log(a);
/*
GO = {
a: undefined -> function a(){} -> 1
}
*/
var b = 3;
console.log(a);//function a() {...}
function a(a) {
console.log(a);//function a() {}
var a = 2;
console.log(a);//2
function a() {}
var b = 5;
console.log(b);
}
a(1)
/*
GO = {
b: undefined,
a: function a() {...}
}
AO = {
a: undefined -> 1 -> function a() {}
b: undefined -> 5
}
*/
a = 1;
function test() {
console.log(a);// undefined
a = 2;
console.log(a);//2
var a = 3;
console.log(a); //3
}
test()
var a;
/*
GO = {
a: undefined -> 1
test: function test() {...}
}
AO ={
a: undefined -> 2 -> 3
}
*/
function test() {
console.log(b);//undefined
if (a) {
var b = 2;
}
c = 3;
console.log(c);//3
}
var a;
test()
a = 1;
console.log(a);//1
/*
GO = {
a: undefined -> 1
test: function test(){...}
c: 3
}
AO = {
b: undefined
}
*/
注意:预编译期间不看if
a = 1;
function test(e) {
function e(){}
arguments[0] = 2;
console.log(e); //2
if(a) {
var b = 3;
}
var c;
a = 4
var a;
console.log(b);//undefined
f = 5;
console.log(c);//undefined
console.log(a);//4
}
var a;
test(1)
/*
GO = {
a: undefined -> 1
test: function test(){}
f: 5
}
AO = {
e: undefined -> 1 -> function e(){} -> 2
b: undefined
c: undefined
a: undefined -> 4
}
*/
5.6面试题
if (typeof (a) && (-true) + (+undefined) + '') {
console.log('通过')
} else {
console.log('没通过');
}
/*
解析:
typeof(a)返回字符串undefined所以是真
-true: -1
+undefined: NaN
*/
console.log(!!' ' + !!'' - !!false || '通过')
// true + false - false
6.作用域、作用域链、预编译、闭包基础
6.1作用域([[scope]])
- 函数创建时, 生成的一个JS内部的隐士属性
- 函数存储作用域链的容器, 作用域链里存储着AO/GO, 函数执行完以后, AO是要销毁的, 每次执行函数都会生产一个新的AO, 函数执行完AO便会销毁, 也就是说AO是一个即时的存储容器
function a() {
function b() {
var b = 2;
}
var a = 1;
b()
}
var c = 3;
a()
注:每个函数的作用域链都是包含GO的–每一个函数在被定义的时候就包含了全局执行上下文GO
图解
1.当函数被定义时
每一个函数在被定义的时候它的作用域链都有全局执行上下文(GO)
2.当函数被执行前一刻(预编译)
3.当b函数被定义时
4.当b函数被执行的前一刻(预编译)
5.当b函数被执行结束的时候
6.当a函数被执行结束的时候
全局执行的前一刻生成GO, -> 函数声明已经定义了
当函数被定义的时候已经形成了作用域([[scope]]) -> 作用域链([[scope chian]]) -> GO
当函数被执行的前一刻生成自己的AO
function a() {
function b() {
function c() {
}
c()
}
b()
}
a()
/*
1.a定义 -> a.[[scope]] -> [[scope chian]] -> 0:GO
2.a执行 -> a.[[scope chian]] -> 0:a的AO -> 1:GO
3.b定义 -> b.[[scope]] -> [[scope chian]] -> 0:a的AO -> 1:GO
4.b执行 -> b.[[scope]] -> [[scope chian]] -> 0:b的AO -> 1:a的AO -> 2:GO
5.c定义 -> c.[[scope]] -> [[scope chian]] -> 0:b的AO -> 1:a的AO -> 2:GO
6.c执行 -> c.[[scope]] -> [[scope chian]] -> 0:c的AO -> 1:b的AO -> 2:a的AO -> 3:GO
7.c结束 -> c.[[scope]] -> [[scope chian]] -> 0:b的AO -> 1:a的AO -> 2:GO
8.b结束 -> b.[[scope]] -> [[scope chian]] -> 0:a的AO -> 1:GO -> 同时c.[[scope]]不存在了
9.a结束 -> a.[[scope]] -> [[scope chian]] -> 0:GO -> 同时b.[[scope]]不存在了
*/
6.2闭包
function test1() {
function test2() {
var b = 2;
console.log(a)
}
var a = 1;
return test2()
}
var c = 3;
var test3 = test1()
test3()
图解
1.当test1函数被定义时
2.当test1函数被执行前一刻(预编译), 函数test2被定义
3.当test1函数被执行结束
4.当test3被执行
5.当test3执行结束
当内部函数被返回到外部并保存时,一定会产生闭包,闭包一定会产生原来的作用域链不释放(不销毁),过度的闭包可能会导致内存泄漏,或加载过慢
7.立即执行函数、闭包深入、逗号运算符
7.1立即执行函数
自动执行, 执行完成后立即释放
// 写法1(看起来比较清晰)
(function() {
})();
// 写法2(w3c建议)
(function(){
}());
// 一定是表达式才能被执行符号()执行
立即执行函数也又返回值, 需要用变量接收
var num = (function (a, b) {
return a + b
}(2, 3));
console.log(num);
()包起来的一定是表达式
函数声明变成表达式的方法: + - ! || &&
function test() {
var arr = [];
for (var i = 0; i < 10; i++) {
arr[i] = function () {
document.write(`${i}->`)
}
}
return arr
}
var myArr = test()
console.log(myArr);
for (j = 0; j < 10; j++) {
myArr[j]()
}
//打印结果为10个10
// 因为return arr形成了闭包
var fn = (
function a() {
return 1
},
function b() {
return '1'
}
)()
console.log(typeof(fn))//str
var a = 10;
if(function b(){}) {
a += typeof(b)
}
console.log(a)//
/*
(function b(){})因为被括号包起来了,所以变成了表达式,函数名就忽略了,所以typeof b是undefined
*/
8.对象、构造函数、实例化
8.1对象
var person = {
name: '张三,
age: 14,
sex: '男',
teach: function() {
console.log('1')
}
}
delete person.sex
delete person.teach
对象内部的函数最好叫方法
delete用于删除对象中的属性
// 对象字面量
var obj = {
name: '张三',
age: 19
}
obj.age = 20
8.2构造函数
// 系统自带的构造函数 -> 与对象字面量是一样的
var obj = new Object();
obj.name = '张三';
obj.sex = '男'
// 自定义构造函数
function Teacher() {
this.name = '冷然';
this.age = 18;
this.weight = 120
this.smoke = function () {
this.weight--
console.log(this.weight)
}
this.eat = function () {
this.weight++
console.log(this.weight)
}
}
var teacher1 = new Teacher()
var teacher2 = new Teacher()
teacher1.smoke()
teacher1.smoke()
console.log(teacher2.weight);
实例化的每个构造函数相互都是不关联的
// 自定义构造函数传参
function Teacher(name, sex, weight) {
this.name = name;
this.sex = sex;
this.weight = weight
}
var teacher1 = new Teacher('张三', '男', 120)
var teacher2 = new Teacher('李四', '女', 90)
console.log(teacher1);
console.log(teacher2);
// 最优写法
function Teacher(option) {
this.name = option.name;
this.sex = option.sex;
this.weight = option.weight
}
var teacher1 = new Teacher({
name: '刘能',
sex: '男',
weight: 140
})
var teacher2 = new Teacher({
name: '赵四',
sex: '女',
weight: 125
})
console.log(teacher1);
console.log(teacher2);
9.构造函数及实例化原理、包装类
9.1构造函数中的this
实例化之前this指向window 实例化之后this指向为当前实例化对象而不是构造函数本身
function Car(color, brand) {
this.color = color;
this.brand = brand;
// 隐士加了return this
}
var car1 = new Car('red', 'Benz')
/*
1.当构造函数被实例化的时候, 就相当于普通函数执行
2.当AO生成后内部默认有个this:{}
GO = {
Car: function Car(){...}
car1: undefined -> { color: 'red', brand: 'Benz'}
}
AO = {
this: {
color: color,
brand: brand
}
}
*/
// 模拟构造函数
function Car(color) {
var me = {}
me.color = color
return me
}
var car = Car('red')
9.2包装类
一般情况原始值并没有自己的方法和属性
var a = 1;
console.log(a); //1
var b = new Number(a)
console.log(b); //Number {1}
b.name = '冷然'
console.log(b); //Number {1, name: "冷然"}
console.log(b + 1); // 参与运算时b会回到原始值
// 经过new Number后就实例化了一个对象, 就叫数字对象/对象字
- new Number() 转换成数字对象
- new String() 转换成字符串对象 ->有length属性
- new Boolean() 转换成布尔对象
null undefined是不可设置任何的属性和方法的
// JS包装类问题
var a = 123;
a.len = 3;
console.log(a.len)//undefined
/*
1.首先看见a是个原始值
2.然后给a加了个len的属性,js引擎认为你不对
3.new Number(123).len = 3 但是保存不了所以只能 delete
4.因为已经变成数字对象的所以打印a.len是undefined
*/
// 数组截断方法
var arr = [1, 2, 3, 4]
arr.length = 2;
console.log(arr) //1,2
9.3面试题
// 1.
var name = 'lengran'
name += 10 //lengran10
var type = typeof(name)//string
//var type = new String(typeof(name))
if(type.length === 6) {
// new String(type).text = 'string'但是无法保存所delete
type.text = 'string'
}
console.log(type.text);//undefined
// 2.
var x = 1,
y = z = 0;
function add(n) {
return n = n + 1;
}
y = add(x)
function add(n) {
return n = n + 3;
}
z = add(x)
console.log(x, y, z);
/*
GO = {
x: undefined -> 1
y: undefined -> 0 -> 4
z: 0 -> 4
add: function add(){n + 1} -> function add(){n + 3}
}
*/
// 3.哪个能打印12345
function foo1(x) {
console.log(arguments);
return x
}
foo1(1, 2, 3, 4, 5)
function foo2(x) {
console.log(arguments);
return x
} (1, 2, 3, 4, 5);
(function foo3(x) {
console.log(arguments);
return x
})(1, 2, 3, 4, 5)
// 4.
function b(x, y, a) {
a = 10
console.log(arguments[2]);
}
b(1, 2, 3)
10.原型、原型链、闭包和立即执行函数、插件开发初识
10.1原型
原型(prototype)其实是function对象的一个属性, 但是prototype也是个对象
function Handphone(color, brand) {
this.color = color
this.brand = brand
this.screen = '18:9'
this.system = 'Android'
}
Handphone.prototype.rom = '64G'
Handphone.prototype.ram = '6G'
Handphone.prototype.screen = '16:9'//自身有的属性不会查找原型
var phone1 = new Handphone('red', '小米')
var phone2 = new Handphone('black', '华为')
console.log(phone1)
console.log(phone2)
所有被构造函数构造出来的对象都可以继承原型上的属性和方法
// 动态参数写在构造函数内部
function Handphone(color, brand) {
this.color = color
this.brand = brand
}
// 静态参数写在原型上继承就可以了(或者一些方法)
Handphone.prototype.rom = '64G'
Handphone.prototype.ram = '6G'
Handphone.prototype.screen = '18:9'
Handphone.prototype.system = 'Android'
Handphone.prototype.call = function() {
console.log('我正在打电话')
}
var phone1 = new Handphone('red', '小米')
var phone2 = new Handphone('black', '华为')
console.log(phone1)
console.log(phone2)
// 简化prototype写法
Handphone.prototype = {
rom: '64G',
ram: '6G',
screen: '18:9',
system: 'Android',
call: function() {
console.log('我正在打电话')
}
}
通过实例化对象不能对prototype进行增删改
function Handphone(color, brand, system) {
this.color = color
this.brand = brand
this.system = system
}
functin Test(){}
Handphone.prototype = {
constructor: Test
}
console.log(Handphone.prototype);
Handphone.prototype上的constructor指向构造函数本身(可以进行修改)
function Car() {
/* __proto__是属于实例化对象的
var this = {
__proto__: Car.prototype
}
*/
}
Car.prototype.name = '宝马'
var car = new Car()
console.log(car)
__proto__是属于每个实例化对象的, proto就是键名用来保存prototype
function Person() {}
Person.prototype.name = '张三'
var p1 = {
name: '李四'
}
var person = new Person()
console.log(person.__proto__);
person.__proto__ = p1
console.log(person.__proto__);
__proto__是可以被修改的但是没有意义
原型本身也有它自己的原型
10.2. 闭包的另一种方式
function test() {
var a = 1;
function plus1() {
a++
console.log(a);
}
window.plus = plus1
}
test()
plus()
10.3.插件
防止全局和函数作用域污染, 因为ES5没有块级所以用立即函数隔离
// js插件写法
//;作用:写多个立即执行函数不报错
;(function () {
function Test() {
this.name = 1234
}
window.Test = Test
})()
var test = new Test()
console.log(test);
11.原型与原型链深入、对象继承
11.1原型链
function Professor() {}
Professor.prototype.tSkill = 'JAVA'
var professor = new Professor()
function Teacher(){
this.mSkill = 'JS/JQ'
}
Teacher.prototype = professor
var teacher = new Teacher()
function Student() {
this.pSkill = 'HTML/CSS'
}
Student.prototype = teacher
var student = new Student()
console.log(student);
沿着原型(_proto)一级一级继承查找叫做原型链
原型链的顶端是Object().prototype
子原型不能修改父原型的原始值 引用值可以修改
普通函数默认返回undefined 构造函数通过实例化以后默认返回this
11.2Object.create(对象 || null)
创建对象, 自定义原型
function Test() {};
Test.prototype.num = 1;
var obj1 = Object.create(Test.prototype)
var obj2 = new Test()
console.log(obj1);
console.log(obj2);
不是所有对象都继承与Object.prototype, Object.create(null)不继承
undefined null 不可以使用toString方法
11.3.call/apply
function test() {
console.log(1)
}
// 系统隐士的加了个call
test.call()
call/apply都是更改this指向唯一的区别就是apply后面的参数以数组传递
function Car(brand, color) {
this.brand = brand;
this.color = color;
this.run = function() {
console.log('running');
}
}
var newCar = {
cc: 3.0
}
Car.call(newCar, 'Benz', 'red')
Car.apply(newCar, ['Benz', 'red'])
console.log(newCar);
var car = new Car('Benz', 'red')
console.log(car);
// call/apply例子
function Compute() {
this.plus = function (a, b) {
console.log(a + b);
}
this.minus = function (a, b) {
console.log(a - b);
}
}
function FullCompute() {
Compute.apply(this)
this.mult = function (a, b) {
console.log(a * b);
}
this.div = function (a, b) {
console.log(a / b);
}
}
var fullCompute = new FullCompute()
fullCompute.plus(4, 2)
fullCompute.minus(4, 2)
fullCompute.mult(4, 2)
fullCompute.div(4, 2)
12.对象继承深入、call_apply、圣杯模式、构造函数和闭包、企业模块化
12.1.解决原型链继承和隔离的问题
如果teacher的prototype和student的prototype直接相等的话, student修改原型也会同样修改teacher的原型,
解决方法如下, 用一个中间实例对象这就叫做圣杯模式
function Teacher() {
this.name = '张三',
this.tSkill = 'JAVA'
}
Teacher.prototype = {
pSkill: 'JS/JQ'
}
var t = new Teacher()
console.log(t);
function Student() {
this.name = '李四'
}
function Buffer() {}
Buffer.prototype = Teacher.prototype
var buffer = new Buffer()
Student.prototype = buffer
Student.prototype.age = 18
var s = new Student()
console.log(s);
// 封装
Teacher.prototype.name = '刘能 ';
function Teacher(){}
function Student(){}
inherit(Student, Teacher)
Student.prototype.age = '18 '
var t = new Teacher()
var s = new Student()
console.log(t);
console.log(s);
function inherit(target, origin) {
function Buffer() { }
Buffer.prototype = origin.prototype;
target.prototype = new Buffer()
target.prototype.constructor = target
target.prototype.super_calss = origin
}
// 企业级封装
var inherit = (function() {
var Buffer = function(){}
return function(target, origin) {
Buffer.prototype = origin.prototype;
target.prototype = new Buffer()
target.prototype.constructor = target;
target.prototype.super_calls = origin
}
})()
13.链式调用、对象属性与遍历、this指向、caller、callee
13.1.链式调用
var sched = {
wakeup: function() {
console.log('跑步')
return this
},
morning: function() {
console.log('吃饭')
return this
},
night: function() {
console.log('睡觉')
}
}
sched.wakeup().morning()
13.2.对象枚举/遍历
var car = {
brand: 'Benz',
color: 'red',
displacement: '3.0',
lang: '5',
width: '2.5'
}
for( var key in car) {
console.log(`${key}:${car[key]}`);
}
13.hasOwnProperty
找对象自身的属性,排除原型的属性
function Car() {
this.brand = 'Benz';
this.color = 'red';
this.cc = '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(car[key]);
}
}
// 第二种查找对象里是否有某个属性
function Car() {
this.brand = 'Benz';
this.color = 'red';
}
Car.prototype = {
width: 2.5
}
// in是不排除原型的
console.log('width' in car)
13.4.instanceof
判断这个对象是否是该构造函数实例化出来的
function Car() {}
var car = new Car()
console.log(car instanceof Car);//true
function Person() {}
var p = new Person()
console.log(car instanceof Person)//false
console.log(car instanceof Object)//true
console.log([] instanceof Array)//true
console.log([] instanceof Object)//true
console.log({} instanceof Object)//true
// 第二种方法 常用
var a = []
var str = Object.prototype.toString.call(a)
if(str === '[object Array]') {
console.log('是数组')
} else {
console.log('不是数组')
}
13.5.this
普通函数和全局的this默认指向window
function test(b) {
this.d = 3; //window.d = 3
var a = 1;
function c() {}
}
test(123)
/*
AO = {
arguments: [123]
this: window
b: undefined -> 123
a: undefined
c: function c(){}
}
*/
function Test() {
/*
*var this. = {
* __proto__: Test.prototype
*}
*
*/
this.name = '123'
}
var test = new Test()
/*
* AO= {
* this: {
* name: '123',
* __proto__: Test.prototype
* }
* }
*
*
* GO = {
* Test: function() {}
* test: {
* name: '123'
* __proto__: Test.prototype
* }
* }
*/
13.6.call/apply
function Person() {
this.name = '张三',
this.age = 18
}
function Programmer() {
Person.apply(this)
this.work = 'Programmer'
}
var p = new Programmer()
console.log(p)
13.7.callee/caller
function test() {
console.log(arguments.callee)
console.log(arguments.callee.length)//形参
console.log(test.length)//形参
console.log(arguments.length)//实参
}
function sum(n) {
if(n <= 1) return 1;
return n + sum(n - 1)
}
sum()
var sum = (function(n) {
if(n <= 1) return 1;
return n + arguments.callee(n - 1)
})(10)
console.log(sum);
caller调用当前函数的函数引用,如果是全局作用于中调用当前的函数就返回null, 严格模式下会报错
// A函数调用B函数(b中有caller),则caller返回值为A函数
function test1() {
test2()
}
function test2() {
console.log(test2.caller)
}
13.8.1typeof返回值
number str obj(null) boolean undefined function\
13.8.2打印结果
undefined == null //true
undefined === null//false
isNaN('100')//false
parseInt('1a') == 1//true
var a = 5;
function test() {
a = 0;
console.log(a);
console.log(this.a);
var a;
console.log(a);
}
test()//0 5 0
new test()//0 undefined 0
14.三目运算、对象克隆、浅拷贝、深拷贝
14.1三目运算符/三元运算符
var a = 5,
str = '';
str = a > 0 ? (a > 3 ? '大于3' : '小于等于3') : '小于等于0'
14.2.浅拷贝/浅复制/浅克隆
浅拷贝: 只能处理第一层属性, 不能拷贝内层引用值
Object.prototype.num = 1;
var person1 = {
name: '张三',
age: 18,
son: {
first: 'Tom',
second: 'Lucy'
}
}
var person2 = clone(person1)
person2.name = '李四'
person2.son.third = 'Ben'
console.log(person1);
console.log(person2);
function clone(origin, target) {
var tar = target || {}
for (var key in origin) {
if (origin.hasOwnProperty(key)) {
tar[key] = origin[key]
}
}
return tar
}
14.3.深拷贝/深复制/深克隆
Object.prototype.num = 1;
var person1 = {
name: '张三',
age: 18,
children: {
first: {
name: '刘能',
age: 40
},
second: {
name: '赵四',
age: 46
}
},
car: ['奔驰', '宝马']
}
var person2 = deepClone(person1)
person2.car.push('奥迪')
console.log(person1);
console.log(person2);
function deepClone(origin, target) {
var target = target || {},
toStr = Object.prototype.toString,
arrType = '[object Array]';
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] = {}
}
deepClone(origin[key], target[key])
} else {
target[key] = origin[key]
}
}
}
return target
}
15.深拷贝实例、数组基础、数组方法、数组排序
15.1.数组基础
本章方法都是操作原数组
// 1.数组字面量-常用
var arr1 = [];
// 2.内置构造函数声明
var arr2 = new Array();
// 3.不使用的
var arr3 = Array()
所有数组都继承于Array.prototype, index: 数组元素的下标(索引值), 从零开始
15.2.稀松数组
var arr = [,1, 3, 5, 7,]
var arr1 = new Array(5)//[empty*5]
console.log(arr);//[empty,1, 3, 5, 7]
console.log(arr.length);//5
var arr3 = [1,2,3,4,5,6,7,8,9,10]
console.log(arr3[10]);//undefined
- new Array(5): 创建一个长度为5的空数组
- new Array(5.2): 报错length属性不正确
- new Array(a): a is notdefined
- new Array(‘a’): 创建一个数组[‘a’]
- new Array(1,2,3): 创建一个数组[1,2,3]
- new Array(,1,3,): 语法错误
15.3.push/unshif
- push在数组最后一位加, 返回值是执行了方法后数组的长度 可以加多个值
- unshif在数组最前面加, 返回值是执行了方法后数组的长度 可以加多个值
var arr = [1, 2, 3]
arr.push(4)
arr.push(4, 5, 6)
Array.prototype.myPush = function() {
for(var i = 0; i < arguments.length; i++) {
this[this.length] = arguments[i]
}
return this.length
}
console.log(arr.myPush(4, 5, 6));
15.4.pop/shift/reverse
- pop 删除数组最后一位并且返回删除的元素
- shift 删除数组第一位并且返回删除的元素
- reverse 反转数组
15.5.splice
- index:参数1 从何处添加或删除元素(开始项下标)
- howmany:参数2 剪切长度,必须是数字,可以为0(为0则不剪切)
- item*n: 要添加到数组的新元素(从剪切位置开始添加)
返回值被删除的元素
var arr = ['a', 'b', 'c']
console.log(arr.splice(1, 1, 1,2,3));
console.log(arr);
var arr = ['a', 'b', 'c', 'e']
arr.splice(3, 0, 'd')
arr.splice(-1, 0, 'd')
console.log(arr);
15.6.sort
用于对数组进行排序(按照ASCII码来排列), 默认升序(字母和数字)
var arr = [27, 49, 5, 7]
//必须有返回值: 1.负值 a就排前面 2.正值 b就排前面
arr.sort(function (a, b) {
// if(a > b) {
// return 1
// } else {
// return -1
// }
// 升序
return a - b;
// 降序
return b - a;
})
console.log(arr);
// 随机排序
var arr = [1, 2, 3, 4, 5, 6]
arr.sort(function(a,b) {
// var rand = Math.random()//返回0-1但不包含0和1
// return rand - 0.5 > 0 ? 1 : -1
return Math.random() - 0.5;
})
console.log(arr);
15.7.面试题
function Foo() {
getName = function() {
console.log(1);
}
return this
}
Foo.getName = function () {
console.log(2);
}
Foo.prototype.getName = function() {
console.log(3);
}
var getName = function() {
console.log(4);
}
function getName() {
console.log(5);
}
// 相当于调用Foo对象下面的getname所以是2
Foo.getName()//2
getName()//4
// 执行了Foo后getname重新赋值了
Foo().getName()//1
getName()//1
// 点优先级高于new,相当于new 2new就没用可以不看了
new Foo.getName()//2
// new Foo()括号优先级最高, 所以带着new一起执行了
//因为本身this没getname所以拿了原型的
new Foo().getName()//3
// 同上第一个new直接忽略了
new new Foo().getName()//3
16.数组方法、类数组
concat
concat链接两个或多个字符串或数组
本章方法都是生成一个新数组
var arr1 = ['a', 'b', 'c']
var arr2 = ['d', 'e', 'f']
var arr3 = arr1.concat(arr2)
console.log(arr1);
console.log(arr3);
slice
在数组中读取指定元素
var arr = [1, 2, 3, 4, 5]
console.log(arr.slice(0,4));
- start: 开始读取下标(包含当前下标数) 不传截取到最后, 负数从后面开始
- end 结束下标(不包含当前下标数) 不传截取到最后
join
把数组中的所有元素转换为一个字符串,参数就是元素分隔符(可选)
var arr = ['a', 'b', 'c', 'd']
var str1 = arr.join('-')
var arr1 = str1.split('-')
console.log(arr1);
split
var arr = ['a', 'b', 'c', 'd']
var str1 = arr.join('-')
var arr1 = str1.split('-', 2)
console.log(arr1);
把一个字符串分割成字符串数组
- 参数1 分隔符
- 参数2 指定返回数组的最大长度(length)
类数组
类数组的原型是Obejct而数组的原型是Array
function test() {
console.log(arguments);
}
test(1,2,3,4,5)
var oDiv = document.getElementByTagName('div')
笔试题
// 数组去重
Array.prototype.unique = function() {
}