浏览器
-
主流浏览器
- IE Trident
- Firefox Gecko
- Safari Webkit
- Chrome Webkit/Blink
- Opera Presto
-
浏览器组成部分
- shell 部分
- 内核部分
- 渲染引擎(语法规则和渲染)
- js 引擎
- 其他模块
JavaScript基础知识
引入方式
内部脚本
外链脚本
WEB标准
W3C标准:结构、样式、行为相分离,通常都会采用外链的方式
变量(varible)
-
变量声明
-
声明、赋值分解
var a; //变量声明 a = 1000; //变量赋值
-
单一
var
var a, b, c = 300, d = 400, e = 500; a = 100; b = 200; //...
-
-
命名规则
- 变量名必须以 英文字母 、_ 、$ 开头
- 变量名可以包括 英文字母、_ 、$、数字
- 不可以使用系统的 关键字、保留字 作为变量名
值类型 - 数据类型
- 原始值
- Number 数值类型
- String 字符串类型
- Boolean 布尔类型
- Undefined 使用的变量定义后未赋值
- Null 空值,对象占位符
- 引用值
- Array 数组类型
- Object 对象
- Function 函数
- Date 时间函数
- RegExp 正则表达式
- 两种值得区别
原始值存储于栈(stack)内存中 先进后出(first in last out)
引用值大部分存储于堆(heap)内存中 (先进先出)
Error
语法错误
逻辑错误
一个代码块发生错误,不会影响其他代码块的执行
运算操作符
-
“ + ”
- 数学运算、字符串连接
- 任何数据类型与字符串相加都等于字符串
-
“ - “,” * “,” / “," % ",” = “,” () “
- 与数学运算相同
- ” () “ 优先级最高,” = “ 优先级最低
” ++ “,” -- “,” += “,” -= “,” /= “,” *= “,” %= “
比较运算符
” > “,” < “,” == “,” >= “,” <= “,” != “
比较的结果为 boolean 类型
字符串比较的是 ascii 码
逻辑运算符
-
” && “,” || “,” ! “
data && console.log('数据传递失败'); //短路语句
var a = 0 || false || 1; var handleClick = function(e) { var event = e || window.event; // 做IE浏览器的兼容 }
-
被认为 false 的值
undefined
null
NaN
""
0
false
条件语句
if
if else
while
do-while
switch case
break
continue
typeof 六种数据类型
number
string
boolean
-
object
- 数据、对象和null都会返回object (null 最初是对象占位符,属于历史遗留问题)
undefined
function
类型换换
-
显示类型转换
- Number(num); 将其参数转换为数值类型并返回。 null = 0, undefined = NaN, "123abc" = NaN
- parseInt(num,基底); 将第一个参数转换为整数类型并返回,第二个参数可以表明第一个参数的基底,然后转换为十进制,进行到非数字位截止。 true/false = NaN, 123.9 = 123 , 123abc = 123
- parseFloat(num); 转换成浮点类型,识别到非数字位并且只识别一个小数点
- String(str); 转换为字符串
- Boolean(); 转换为布尔类型
- toString(redix); 是用 “.” 的方式调用当前方法,转换成字符串类型, radix 转换成目标进制。 undefined / null 不可以使用当前方法
-
隐式类型转换
-
isNaN(); 判断是不是不是一个数
isNaN('abc') -隐式调用-> Number('abc') =然后和NaN进行比较= NaN? true:false;
-
++/-- 、+/-(一元正负)
//自动隐式调用Number(),强制转换成Number类型 var a = "123"; a++; // a = 124 var b = "abc"; b++; // b = NaN
-
当加号两边有一个是字符串的,则将两边都转换为字符串并连接,如果是数值就做加法运算
-
-、*、/、%
隐式调用Number()方法,
&&、||、!
-
<、>、<=、>=
比较时数字优先
-
==、!=
数值类型
-
-
不发生类型转换
- ===
- !==
函数
-
定义
-
函数声明
function function_name() { }
-
函数表达式
// 命名函数表达式 var function_name = function test() { // test不代表函数体,在函数中 function_name.name == test console.log(function_name.name); //test } // 匿名函数表达式 ** 常用 推荐 ** var function_name = function() { }
-
-
组成形式
-
函数名称
- 命名规则和变量一致
- 多个单词使用小驼峰命名法
theIsFunctionName
-
参数
-
形参
// 传递形式参数(形参) function function_name(a, b) { // 在括号中传递a, b就相当于 隐式的在函数中 var a; var b; }
-
实参
// 传递实际参数(实参) function_name(1, 2);
-
arguments 实参列表
一个类数组,保存传进来的实参
function function_name(a, b, c) { console.log(arguments); // 打印实参列表 console.log(function_name.length); // 计算形参的长度 } function_name(1, 2, 4)
-
-
return
-
终止函数
return 之后的语句将不会得到执行
-
返回值
将内部值返回到外部
-
-
递归
// 1. 找规律 2. 找出口 // 使用递归实现阶乘和斐波那契数列 function mul(n) { if(n == 1 || n == 0) { return 1; } return n * mul(n - 1); } console.log(mul(5)) function fb(n) { if(n == 1 || n == 2) { return 1; } return fb(n-1) + fb(n-2); } fb(10);
JS执行三部曲(预编译)
语法分析(通篇扫描是否存在语法错误)
-
预编译
预编译发生在函数执行的前一刻
函数声明整体提升,变量 声明提升,提升到逻辑的最前
不够严谨
-
预编译前奏
-
imply global 暗示全局变量:即任何变量,如果变量未经声明就赋值,此变量就为全局对象所有。
a = 123; // a 为全局变量 function ao() { var a = b = 123; // b 为暗示全局变量 }
-
一切声明的全局变量,全是window的属性。
-
-
预编译四部曲
- 创建AO()对象
- 将形参和变量声明作为AO对象的属性,并赋值undefined
- 将实参和形参相统一
- 将函数声明作为AO对象的属性,并将函数体作为它的的值
function fn(a) { console.log(a); // fn() {} var a = 123; console.log(a); // 123 function a() {}; console.log(a); // 123 var b = function() {}; console.log(b); // fn() {} function d() {}; } fn(1); /* 1.创建AO对象 AO{ } 2.将形参和变量声明作为AO对象的属性,并赋值 undefined AO{ a : undefined 形参和变量相同时后者覆盖 b : undefined } 3.将实参和形参相统一 AO{ a : 1, b : undefined } 4.将函数声明作为AO对象的属性,并赋值函数体 AO{ a : fn(){} b : undefined d : fn(){} } */
-
解释执行
作用域
-
作用域初探
作用域定义:变量(变量作用域又称上下文)和函数生效(能被访问)的区域
-
全局变量
-
局部变量
-
访问顺序
内部可以访问外部变量,外部不可以访问内部变量,自内向外
-
-
作用域精解
-
执行期上下文
当函数执行时,会创建一个称为 执行期上下文 的内部对象。一个执行期上下文定义了一个函数执行时的环境,函数每次执行时对应的执行期上下文都是独一无二的,所以多次调用一个函数会导致创建多个执行期上下文,当函数执行完毕,它所产生的执行期上下文被销毁
-
查找变量
从作用域的顶端依次向下查找
-
[[ scope ]]
每个javascript函数都是一个对象,对象中有些属性我们可以访问(例如:
function_name.name
),但有些不可以,这些属性仅供javascript引擎存取,[[ scope ]] 就是其中一个。[[ scope ]] 指的就是我们所说的作用域,其中存储了执行期上下文的集合。
-
作用域链
[[ scope ]] 中所存储的执行期上下文对象的集合,这个集合呈链式链接,我们把这种链式链接叫做作用域链
// 每个函数被创建时,会根据当前的执行环境生成作用域链,保存在当前函数的 [[ scope ]] 属性作用域链中 // 当函数被执行时,会生成自己的作用域放在[[ scope ]]属性作用域链的最顶端 function a() { // 1. a 函数被定义 [[ scope ]] 0 : GO function b() { // 3. b 函数被定义,拿到当前执行环境的作用域链 [[ scope ]] 0 : AO(a 函 数的AO), 1 : GO var b = 234; } var a = 123; b(); // 4. b 函数被执行,生成自己的执行期上下文(AO对象),存进 [[ scope ]]作 用域链中 0 : AO(b 函数的), 1 : AO(a 函数的), 2 : GO } var glob = 100; a(); // 2. a 函数 被执行 [[ scope ]] 0 : AO(a 函数的AO对象), 1 : GO
-
-
总结案例
function a() { function b() { function c() { } c(); } b(); } a(); // a defined a.[[ scope ]] --> 0 : GO // a doing a.[[ scope ]] --> 0 : aAO // 1 : GO // a 的执行,产生了 b 的定义 // b defined b.[[ scope ]] --> 0 : aAO // 1 : GO // b doing b.[[ scope ]] --> 0 : bAO // 1 : aAO // 2 : GO // b 的执行,产生了 c 的定义 // c defined c.[[ scope ]] --> 0 : bAO // 1 : aAO // 2 : GO // c defined c.[[ scope ]] --> 0 : cAO // 1 : bAO // 2 : aAO // 3 : GO // 所有的 GO、aAO、bAO、cAO 都是同一个执行上下文 // 当一个函数执行完毕时,相对应的执行上下文也会跟着销毁
闭包
-
定义
当内部函数被保存到外部时,将生成闭包。闭包会导致原有作用域链不释放,造成内存泄漏。
-
作用
-
实现公有变量(累加器)
function add() { var count = 0; function demo() { count ++; console.log(count); } return demo; } var counter = add(); counter(); counter(); counter();
-
做缓存(存储结构)
function test() { var num = 100; function a() { num ++; } function b() { num --; } return [a, b]; } var myArr = test(); myArr[0](); myArr[1](); // ----------------------------------------------------------------------------- function eater() { var food = ""; var obj = { eat : function(){ console.log("i am eating" + food); }, push : function(myFood) { food = myFood; } } return obj; } obj.eat();
-
实现封装,属性私有化
模块化开发,防止污染全局变量
-
-
案例
function a() { var num = 100; function b() { num ++; console.log(num); } return b; // 将 b 返回到外部,导致原有的作用域链不释放 } var demo = a(); demo(); //101 demo(); //102
立即执行函数
-
定义
此类函数没有声明,在一次执行过后释放。适合初始化工作
-
语法
// 立即执行函数执执行完之后立即销毁,初次之外和普通函数没有任何不同 var num = (function (a, b, c) { return a + b + c; }(1, 2, 3))
-
拓展
// 立即函数不同写法 (function () {}()); // W3C推荐 (function () {})(); // 只有表达式才可以被执行符号执行 function test() { console.log('a'); }(); // 语法错误,此种方式不可以被执行,这种叫做函数声明 // 能被执行符号执行的函数,这个函数的名字就会被自动忽略 var a = function() { // a 不再代表函数了 console.log('a'); }(); // 可以,函数表达式 // ()、+(正)、-(负)、!、&&、|| 都可以将一个函数转换为表达式 - function() { console.log('a'); }(); // ... // !!! 注意 function test(a, b, c) { // 在这中情况下,系统不会报错,但也不会执行 console.log(a, b, c) }(1, 2, 3); // 系统会自动识别成这样 function test(a, b, c) { console.log(a, b, c); } (1, 2, 3); // 不识别成执行符号,看做一个独立的**逗号表达式(知识点再后续文档中呈现)**
闭包补充
-
闭包的防范
闭包会导致多个执行函数共用一个公有变量,如果不是特殊需求,尽量防止这种情况发生。
-
经典案例
// 给数组的每一位绑定一个函数,并打印出当前下标 function test() { var arr = []; for(var i = 0; i < 10; i ++) { arr[i] = function () { // 赋值函数 document.write(i + " ") } } return arr; // 返回到外部,形成了闭包 } var myArr = test(); for(var j = 0; j < 10; j ++) { myArr[j](); } // 执行结果 10 10 10 10 10 10 10 10 10 10 // 形成了闭包,由于在执行数组中的函数时,test 已经执行完毕。 i = 10并不再循环,这是for的判断条件 // 形成闭包执行,这10个函数访问的 i 在 test的AO里都已经变成了10 // 解决方法 function test() { var arr = []; for(var i = 0; i < 10; i ++) { (function(n) { // 使用立即执行函数 arr[n] = function() { document.write(n + " ") } }(i)) } return arr; } var myArr = test(); for(var j = 0; j < 10; j ++) { myArr[j](); }
逗号操作符
// 逐个审查每一位元素。如果某位元素需要计算,则计算该元素。最后,返回最后一个元素的计算结果
var a = (2, 3);
var f = (
function f() {
return '1';
},
function g() {
return 2;
}
)();
typeof(f);
对象
-
是一种基础的变量类型,属于引用值
var mrZhang = { name : 'zs', age : 22, sex : 'male', health : 100, smoke : function() { console.log('I am somkeing'); mrZhang.health --; // this.health this表示当前,第一人称 } }
-
属性的增、删、改、查
-
增
mrZhang.wife = 'xiaoliu'
-
删
delete mrZhang.wife
-
改
mrZhang.sex = 'female'
-
查
mrZhang.name
-
-
对象的创建方法
-
字面量
var obj = {}; // plainObject 对象字面量 / 对象直接量
-
构造函数
-
系统自带
new Object(); // 得出相同,且相互独立的对象 Array(); Number(); Boolean(); string(); Date();
-
自定义
function Car(color) { // 为区分自定义函数,使用大驼峰命名法 this.color = color; this.name = 'BMW'; this.height = '1400'; this.lang = '4900'; this.weight = '1000'; this.health = 100; this.run = function() { this.health --; } } var car1 = new Car('red'); var car2 = new Car('green')
-
-
构造函数的内部原理
// new 之后函数的变化 function Student(name, age, sex) { // var this = {}, AO{ this:{name:'zhagnsan'} } // 隐式创建一个this对象 this.name = name; this.age = age; this.sex = sex; this.grade = 2017; // return this; 隐式 return this; } var student = new Student('zhangsan', 22, 'male'); //------------------------------------------------------------------------------ /* 1. 在函数体最前面隐式的创建 this = {}; 2. 执行 this = xxx; 3. 隐式的 return this; 3.1. 可以手动显示 return ({}, [], /..)类型为obj的值,原始值是无效,自动忽略的 !!!并不是最终形式,后续补充 */ // 模拟构造函数(只是简单模拟,并不推荐使用。因为还有更深层次的东西模拟不了,后续补充) function Person(name, height) { var that = {}; that.name = name; this.height = height; return that; } var person1 = Person('xiaowang', 180);
-
包装类
Number();
String();
-
Boolean();
// 包装类过程 var num = 4; // 原始值是坚决不能用属性的 num.len = 3; // new Number(4).len = 3; // delete console.log(num.len) // new Number(4).len undefined
原型
-
定义:原型式 function 对象的一个属性,它定义了构造函数制造出的对象的公共祖先。通过该构造函数产生的对象,可以继承原型的属性和方法,原型也是对象。
// Person.prototype -- 原型 // Person.prototype = {} 祖先 Person.prototype.name = 'hehe'; function Person() { // this.name = 'zs'; 如何构造函数中存在和原型中相同的属性,会调用构造中的 } var person1 = new Person(); // 都继承了原型的 name 属性 var person2 = new Person(); // 原型的 增、删、改、查 // Person.prototype.sex = 'male' // delete Person.prototype.sex // Person.prototype.sex = 'female'; // Person.prototype.sex = 'male' Person.prototype = { // 也可以这样定义原型 height : 180, //... }
-
利用原型的特点和概念,可以提取共有属性。
Car.prototype = { // 将公有部分提取到原型中 naem : 'BMW', height : 1400 //... } function Car(color) { this.color = color; } var car1 = new Car('red');
-
对象如何查看对象的构造函数 -> constructor
function Car() { } var car1 = new Car(); console.log(car1.constructor); // 这个方法也是在原型中继承过来的,可以手动更改 /* Car.prototype {constructor: ƒ} constructor: ƒ Car() __proto__: Object */
-
对象如何查看原型 -> 隐式属性
__proto__
// 查看 person1.__proto__ Person.prototype.name = 'zs'; function Person() { // var this = { // __proto__ : Person.prototype, 当发生 new 的时候,this对象中就会存在__proto__属性 // 也就是说,如果 person1 查找属性,在Person构造中不 存在的话,就会通过__proto__属性所绑定的原型来查找, 这是系统提供的属性 // } } var person1 = new Person(); console.log(person1.name)
原型链
-
如何构成原型链?
// 手动的更改构造函数的原型,连成一个链,称为原型链 function Grand() { this.grandName = 'grand' } var grand = new Grand(); Father.prototype = grand; function Father() { this.fatherName = 'father'; } var father = new Father(); Son.prototype = father; function Son() { this.sonName = 'son'; } var son = new Son();
-
原型链上属性的增删改查
增删改当前属性只能使用当前属性的原型来操作
-
绝大多数对象最终都会继承自
Object.prototype
// 执行 Object.create() // 错误信息 VM100:1 Uncaught TypeError: Object prototype may only be an Object or null: undefined at Function.create (
) at :1:8 Object.create(null); // 创建出的对象不会继承自 Object.prototype -
Object.create()
// 创建一个对象并指定原型 // var obj = object.create(原型) var obj = {name : 'zs', age : 22, sex : 'male'}; var student = Object.create(obj); Person.prototype.name = 'ls'; function Person() { } var obj1 = Object.create(Person.prototype);