语言
- 编译性语言:通篇翻译,完成后生成一个翻译完的文件,程序会执行这个翻译完的文件。
- 优点:通篇翻译快;
- 缺点:移植性不好(不跨平台)。
- 解释性语言:翻译一行执行一行。
- 优点:跨平台;
- 缺点:翻译一行执行一行“稍微慢”。
总结:
JavaScript是解释性语言。- 单线程(异步):一个执行体同一时间内只能做一件事。
- 多线程(同步):一个执行体同一时间内能做多件事。
JavaScript引擎是单线程。- 计算机里面的同步叫异步,计算机里面的异步叫同步。
JavaScript执行队列(轮转时间片)JavaScript三大部分:ECMAScript,DOM,BOM
主流浏览器及其内核
IE(trident)Chrome(webkit/blink)firebox(Gecko)Opera(prsto)Safari(webkit)
变量
- 变量声明、赋值
var a;
- 命名规则
- 变量名必须以英文字母、_、$开头
- 变量名可以包括英文字母英文字母、_、$、数字
- 不可以用系统的关键字、保留字作为变量名
数据类型
- 基本数据类型
- 原始值(
stack栈)
number、boolean、string、undefined、null
var a = 10;
var b = a;
a = 20;
console.log(a,b) // 20,10
- 引用值(
heap堆)
arr、obj.......
var arr = [1,2];
var arr1 = arr;
arr.push(3);
console.log(arr,arr1) //[1,2,3],[1,2,3]
var arr = [1,2];
var arr1 = arr;
arr = [1,3];
console.log(arr,arr1) //[1,3],[1,2]
js语句基本规则
- 语句后面要分号结束
js语法错误会引发后续代码终止,但不会影响其它js代码块- 书写格式要规范,“=+/-”两边都应该有空格
- 错误分两种
- 低级错误(语法解析错误):
SyntaxError(语法解析错误)、ReferenceError(引用错误) - 逻辑错误(标准错误,情有可原)
js基本运算符
+:数学运算,字符串拼接
-、*、/、%(摩尔)取余、=、()
0 / 0 :NAN(数字类型)
1 / 0 :Infinity(正无穷)
-1 / 0 :-Infinity(负无穷 )
优先级 = 最弱 ()较高
++、--、+=、-=、*=、/=、%=
a++自身加加在赋值给自己
a--自身减减在赋值给自己
var a = 10
console.log(a ++) // 10
console.log(a) // 11
++在后:先执行语句后++
console.log(++ a) // 11
console.log(a) // 11
++在前:先++在执行本条语句
var b = ++a - 1 + a++
console.log(b,"+",a) // 21,12
%=:取余付给自身
条件运算符
NAN不等自己,包括自己- 字符串比较的
ASCLL码 - <、>=、<=、==、!=、>
逻辑运算符
- && || !
- (&&)规则
- 先看第一个表达式如果转换成布尔值的结果,如果为真,那么它会看第二个表达式转化为布尔值的结果,然后如果只有两个表达式的话,只看第二个表达式,就可以返回该表达式的值了。
- 先看第一个表达式如果转换成布尔值的结果,如果为假,直接返回结果
- (||)规则
- 先看第一个表达式如果转换成布尔值的结果,如果为假,那么它会看第二个表达式转化为布尔值的结果,然后如果只有两个表达式的话,只看第二个表达式,就可以返回该表达式的值了。
- 先看第一个表达式如果转换成布尔值的结果,如果为真,直接返回结果
- 注:
undefined,null,NAN,“”,0,false转化为布尔值为false
条件运算符
if(){}
- 在
if语句中写&&的时候,必须全真才执行,||有一个为真就执行
if(){}else{}for(){}while(){}switch(){}
typeof
JavaScript的六种基本数据类型undefiend< 0 //falseundefiend== 0 //false
number、string、boolean、function、undefined、object
- 显示类型转换
Number(mix)转成数字- 特殊:
true ==> 1 、false ==> 0 、null ==> 0 、undefined ==> NAN parseInt(string,radix)- 特殊:
parseInt(string,radix)有两个参数一个需要转换的,另外一个是radix它的范围是2-36 parseFloat(string)用法号parseInt(string)类似toString()和String()转成字符串- 特殊:undefined、nul不能使用toString()
Boolean()转换成布尔值- 特殊:
undefined、null、NAN、“”、0、false转成布尔值为false
- 隐式类型转换
isNAN()==>Number()- ++--、+- ==> Number()
-
- ==>
String()如果加号一边有字符串就会调用String()
- ==>
- -、*、%、\ ==>
Number() - &&、||、! ==>
Boolean() - <、>、>=、<= ==>
Boolean() - ==、!= ==>
Boolean() toFixed()保留几位有效数字
undefined > 0 // false
undefiend < 0 // false
undefiend == 0 // false
null > 0 // false
null < 0 // false
null == 0 // false
null == undefined // true
NAN == NAN // false
- 不发生类型转换
- === 、!==
- 注意:
typeof(typeof(a))==>string
函数
- 定义
- 三种形式
- 函数声明
function test(){}
- 命名函数表达式
var test = function abc(){}
- 匿名函数表达式 -- 函数表达式
var test = function {}
- 组成形式
- 函数名称
function sum(){}
- 参数
// 形参
function sum(a,b){
// arguments 实参列表
if(sum.length > arguments.length){
console.log("形参多了")
}else if(sum.length < arguments.length){
console.log("实参多了")
}else{
console.log("实参和形参相等")
}
}
// 实参
sum(1,2)
// 形参
function sum(){
// arguments 实参列表
}
注意:实参和形参都可以多,函数不会受影响
- 返回值
return终止函数
js三部曲
- 语法分析
- 预编译
- 函数声明整体提升
- 变量 声明提升
- 预编译前奏
imply global暗示全局变量:即任何变量,如果未经声明就赋值,此变量就为全局对象所有- 一切声明的全局变量,全是
Windons的属性
- 预编译四部曲
- 问题:一个变量包含形参名,变量名,函数名一样的问题。解决优先级顺序的问题,包括谁覆盖谁的问题,在包括执行顺序影响变量,函数等。
- 运行期上下文:当函数执行时,会创建一个称为执行期上下文的内部对象。一个执行期上下文定义了一个函数执行时的环境,函数每次执行时对应的执行上下文都是独一无二的,所以多次调用一个函数会导致创建多个执行上下文,当函数执行完毕,它所产生的执行上下文被销毁。
- 查找变量:从作用域链的顶端依次向下查找。
- 创建AO对象(执行期上下文)
- 找形参和变量声明,并且把形参和变量的名作为
AO对象的属性名,值统一为undefined - 实参和形参值 相统一
- 在函数声明找函数声明,把函数声明的名当成
AO对象的属性名,值被覆盖为函数体 - 执行
function fu(a){
console.log(a)
var a = 13;
console.log(a)
function a(){}
console.log(a)
var b = function {}
console.log(b)
function d(){}
}
fu(a)
规律: 如何一个函数里有重名的变量和函数,并且在第一次打印,一定是函数,因为他的优先级最高
- 解释执行
作用域
[[scope]]: 每个javascript函数都是一个对象,对象中有些属性我们可以访问,但有些不可以,这些属性仅供javascript引擎存取,[[scope]]就是其中一个。[[scope]]指的就是我们所说的作用域,其中存储了运行期上下文的集合。- 作用域链:
[[scope]]中所存储的执行期上下文对象的集合,这个集合呈链式链接,我们把这种链式链接叫做作用域链。
function a(){
function b(){
function c(){
}
c()
}
b()
}
a()
解释:
a 定义 --> 0: 生成GO
a 执行 --> 0: 生成aAO
1: 生成GO
b 定义 --> 0: 生成aAO
1: 生成GO
b 执行 --> 0: 生成bAO
1: 生成aAO
2: 生成GO
闭包
- 什么是闭包:当内部函数被保存到外部时,将会生成闭包。闭包会导致原有作用域链不释放,造成内存泄露。
- 作用:
- 1、实现公有变量:累加器
function a(){
var count = 0;
function b(){
count++
console.log(count)
}
return b
}
var d = a();
d();
- 2、可以做缓存(存储结构)
function eater(){
var food = "";
var obj = {
eat: function (){
console.log("I am eat "+ food);
food = "";
},
push: function (myFood){
food = myFood;
}
}
return obj;
}
var eater1 = eater();
eater1.push("苹果");
eater1.eat()
- 3、可以实现封装,属性私有化
function Person(name,age){
var a = '1';
this.name = name;
this.age = age;
this.add = function{
this.age = a;
}
this.changeAdd = function(newValue){
a = newValue;
}
this.sayAdd = function(){
console.log(a)
}
}
var person = new Person('占三','19');
- 4、模块化开发,防止污染全局变量
立即执行函数
什么是立即执行函数:此类函数没有声明,在一次执行过后即释放。适合做初始化工作。
(function (){
}()) // 第一种
(function (){
})() // 第二种
// 只有表达式才能被执行符号执行
执行完了,就销毁了,有返回函数,有执行期上下文,有预编译,函数有的它都有。
总结:什么是闭包?
- 两个或多个函数嵌套,把里面的函数保存到外面,这样的一个情况必然会形成闭包,然后里面的函数在外面执行的时候,一定能调用了原来他在的函数环境里面的变量。
- 当内部函数被保存到外部时,将会生成闭包。闭包会导致原有作用域链不释放, 造成内存泄露。(官方解释)
对象
- 对象组成
- 对象的增删改查
- 对象的创建方法
1、var obj = {} // plainObject 对象字面量
2、构造函数
(1)系统自带构造函数 new Object()
(2)自定义
- 构造函数的内部原理
- 在函数题最前面隐式的加上
this= {} - 执行
this.xxx= xxx; - 隐式的返回
this
// 命名规则:大驼峰
function Student(name ,age ,sex){
// var this = {}
this.name = name;
this.age = age ;
this.sex = sex;
// return this;
}
var stu = new Student('张三','19','男');
- 包装类
new Number()
new String()
new Boolean()
例子:
var num = 8;
num.len = 2;
// 这里使用到了包装类
/*
*new Number(num).len = 2
*delete
*
*new Number(num).len (访问)
*/
console.log(num.len) // undefined
var str = 'abcd';
str.length = 2;
// 这里使用到了包装类
/*
*new String(str).length = 2
*delete
*
*new String(str).length (访问)
*/
console.log(str) // abcd
console.log(str.length) // 4
原型
- 定义:
原型是
function对象的一个属性,它定义了构造函数制造出的对象的公共祖先。通过该构造函数产生的对象,可以继承该原型的属性和方法。并且原型也是对象。
Person.prototype.lastName = "张三";
function Person(name,age,sex){
this.name = name;
this.age = age;
this.sex = sex;
}
var person = new Person('王五','19','男');
person.lastName // "张三"
person.prototype.lastName = '李四';
person.prototype.NewName = '程东';
delete person.prototype.lastName
- 利用原型特点和概念,可以提取共有属性。
// 共有属性
Person.prototype.height = 1400;
Person.prototype.weight = 1000;
function Person(color){
this.color = color;
}
var person = new Person('black');
var person1 = new Person('red');
- 对象如何查看原型--->隐式属性
__proto__ - 对象如何查看对象的构造函数--->
constructor
function Person(){
}
function Car(){
}
Car.prototype = {
constructor:Person
}
var car = new Car()
- 原型链
- 如何构成原型链
- 原型链上属性的增删改查
- 只有本身原型上有的属性才可以增删改查,其他人继承的无法增删改查
- 绝大多数对象最终都会继承自
Object.prototype(重点需要记住) Object.create()8.call和apply
function Person(name,age,sex){
this.name = name;
this.age = age;
this.sex = sex;
}
function Student(name,age,sex,wight,height){
// this.name = name;
// this.age = age;
// this.sex = sex;
Person.call(this,name,age,sex);
Person.apply(this,[name,age,sex]);
this.wight = wight;
this.height = height;
}
var stu = new Student('张三','18','男','180','90');
两者区别:
call和apply是做什么的,有什么区别:call和apply是改变this的指向,传参列表不同call是需要把实参按照形参的个数传进去apply是需要穿一个arguments
继承
- 传统形式 ---> 原型链
## 过多的继承了没有用的属性
Grand.pototype.lastName = '张三';
function Grand(){
}
var grand = new Grand();
Father.pototype = grand;
function Father(){
this.name = "李四";
}
var father = new Father();
Son.pototype = father;
function Son(){
}
var son = new Son();
- 借用构造函数
- 不能继承借用构造函数的原型
- 每次构造函数都要多走一个函数
function Person(name,age,sex){
this.name = name;
this.age = age;
this.sex = sex;
}
function Son(name,age,sex,height){
Person.call(this,name,age,sex)
this.height = height;
}
var son = new Son();
- 共享原理
- 不能随便改动自己的原型
Person.pototype.lastName = '张三';
function Person(){
}
function Son(){
}
// Son.pototype = Person.pototype;// 封装成函数
function extends(newValue,OlbValue){
newValue.pototype = OlbValue.pototype;
}
extends(Son,Father);
// 如果新增son属性,father也会新增属性
son.pototype.age = '18';
var son = new Son();
- 圣杯模式
Person.pototype.lastName = '张三';
function Person(){
}
function Son(){
}
// 基本写法
function extends(newValue,OlbValue){
function F(){};
F.pototype = OlbValue.pototype;
newValue.pototype = new F();
newValue.pototype.constructor = newValue;
// 可以知道它继承谁
newValue.pototype.uper = OlbValue.pototype;
}
// 高级写法
var extends = (function (){
var F = function(){};
return function () {
F.pototype = OlbValue.pototype;
newValue.pototype = new F();
newValue.pototype.constructor = newValue;
// 可以知道它继承谁
newValue.pototype.uper = OlbValue.pototype;
}
}());
extends(Son,Father);
// 如果新增son属性,father这时不会新增
son.pototype.age = '18';
var son = new Son();
命名空间
- 管理变量
- 防止污染全局
- 适用于模块开发
var init = (function(){
var a = 123;
function callNum(){
console.log(a)
}
return function(){
callNum()
}
}())
- 如何模拟jQ连续调用
var obj = {
read:function(){
console.log('I am reading')
return this;
},
sing:function(){
console.log('I am singing')
return this;
},
jump:function(){
console.log('I am jumping')
return this;
}
}
obj.read().sing().jump()
- 属性表示方法
##obj.prop
##obj['prop']
var obj = {
sum1:{name:'1'},
sum2:{name:'2'},
sum3:{name:'3'},
sayNum:function(num){
return this['sum' + mum]
}
}
- 对象枚举
for in
var obj = {
name : 'zhangsan',
age : '19',
sex : '男'
}
// 遍历枚举和对象使用 for in
for (var key in obj) {
console.log(obj[key])
}
注:for xxx in xxx 循环 就是遍历对象用。通过对象属性的个数遍历多少圈。遍历时把对象的属性明放到in前面的xxx里面去,
hasOwnProperty判断对象的属性是否原型链上的
// in 和 hasOwnProperty的区别
// in的功能是判断你能不能用这个属性
// hasOwnProperty的功能是判读这个属性属于不属于该对象
var obj = {
name : 'zhangsan',
age : '19',
sex : '男',
__proto__:{
lastName:'wangwu'
}
}
for (var key in obj) {
if(obj.hasOwnProperty(key)){
console.log(obj[key])
}
}
instanceof
// 数组和对象的如何区分
instanceof的解释:
// A对象 是不是 B构造函数构造出来的
// 看A对象的原型链 有没有B的原型
方法一:
[].constructor // ƒ Array() { [native code] }
{}.constructor// ƒ Object() { [native code] }
方法二:
[] instanceof Array // true
{} instanceof Array // false
{} instanceof Object // true
方法三:
Object.prototype.toString.call([])
Object.prototype.toString.cal l({})
this的指向
- 函数预编译过程的
this指向window - 全局作用域里
this指向window call/apply可以改变函数运行时this的指向- 谁调用函数,函数里面的
this就指向谁
arguments.callee和fun.caller
function test(){
console.log(arguments.callee);
function demo(){
console.log(arguments.callee);
}
demo() // demo
}
test() // test
- 克隆
// 浅克隆
var obj = {
name:"张三",
age:"19",
sex:"男",
}
var obj1 = {}
function clone(origin,target){
var target = target || {};
for(var prop in origin){
target[prop] = origin[prop]
}
return target;
}
clone(obj,obj1)
// 深拷贝
var obj = {
name:"张三",
age:"19",
sex:"男",
num:[1,2,3],
wife:{
son:{
name:'小张'
}
}
}
var obj1 = {}
function deepClone(origin,target){
// 容错
var target = target || {},
toStr = Object.prototype.toString,
arrStr = '[object,Array]';
for(var prop in obj){
// 判断是否是原型链上的值
if(origin.hasOwnProperty(prop)){
// 判断是引用值
if(origin[prop] !== 'null' &&
typeof(origin[prop]) == 'object'){
// 判断是数组还是对象
if(toStr.call(obj[prop]) == arrStr){
target[prop] = []
}else{
target[prop] = {}
}
//递归
deepClone(origin[prop],target[prop])}else{
target[prop] = origin[prop]
}
}
}
return target;
}
数组
- 创建数组
var arr = [](字面量)var arr = new Array(length/content)- 注:
var arr = new Array(10)表示数组长度为10
- 数组的读和写
arr[num]// 不可以溢出读 结果为undefinedarr[num] = xxx;// 可以溢出写
- 数组方法
- 改变原数组:
push、pop、shift、unshift、sort、reverse
var arr = [];
Array.prototype.push = function() {
for(var i = 0; i < arguments.length; i++){
this[this.length] = arguments[i]
}
return this.length;
}
reverse:数组反转(降序)
splice(从第几位开始,截取多少的长度,在切口处添加新数据)
sort数组排序(升序)
例子:
冒泡排序
1.必须写两个形参
2.看返回值
(1)当返回值为负数时,那么前面的数放在前面
(2)当返回值为正数时,那么后面的数放在前面
(3)为0.不动
var arr = [1,3,2,4,6,4];
arr.sort(function(first,second){
// 升序
if(first > second){
return 1;
}else{
return -1;
}
// 降序
if(first < second){
return -1;
}else{
return 1;
}
})
// 通示
var arr = [1,3,2,4,6,4];
arr.sort(function(first,second){
return first - second; // 升序
rerurn second - first; // 降 序
})
// 乱序
var arr = [1,3,2,4,6,4];
arr.sort(function(first,second){
rerurn Math.random() - 0.5;
})
- 不改变原数组:
concat、join、split、toString、slice
- 两个参数:slice(从该位开始截取,截取到该位) 截取
- 一个参数:slice(从该位开始截取一直截取到最后)
- 如果写负数:slice(负数 + length)
- 不写参数:slice()全截取
类数组(必须写length)
var obj = {
"0":'a',
"1":'b',
"2":'c',
"3":'d',
"length": 4,
"push": Array.prototype.push
}
obj.push('e')
var obj = {
"0":'a',
"1":'b',
"2":'c',
"3":'d',
"4":'e',
"length": 5,
"push": Array.prototype.push,
"splice":Array.prototype.splice
}
例子:
var obj = {
"2":'c',
"3":'d',
"4":'e',
"length": 3,
"push": Array.prototype.push
}
obj.push('a')
obj.push('b')
var obj = {
"2":'c',
"3":'a',
"4":'b',
"length": 5,
"push": Array.prototype.push
}
原理:
Array.prototype.push = function(e){
obj[obj.length] = e;
obj.length++;
}
// 属性要为索引(数字)属性,必须有length属性,最好加上push
// 类数组的关键是length
// 好处
- 可以利用属性名模拟数组的特性
- 可以动态的增长
length属性 - 如果强行让类数组调用
push方法,则会根据length属性值的位置进行属性的扩充。
练习
1、封装`typeof`
function type(e) {
// 1、原始值和引用值
// 2、区分引用值
if (e === null) {
return null;
}
if (typeof e === "object") {
if (Object.prototype.toString.call(e) === "[object Array]") {
return "array";
}
if (Object.prototype.toString.call(e) === "[object Object]") {
return "object";
}
if (Object.prototype.toString.call(e) === "[object Boolean]") {
return "boolean - object";
}
if (Object.prototype.toString.call(e) === "[object Number]") {
return "number - object";
}
if (Object.prototype.toString.call(e) === "[object String]") {
return "string - object";
}
} else if(typeof e === 'function') {
return 'function';
} else {
return typeof e;
}
}
function genrate (e, type = 'Array'){
if (Object.prototype.toString.call(e) === `[object ${type}]`) {
return true
}
}
2、数组去重(原型链上数组去重)
Array.prototype.unique = function () {
var temp = {};
var result = [];
for (var i = 0; i < this.length; i++) {
if (!temp[this[i]]) {
temp[this[i]] = true;
result.push(this[i]);
}
}
return result
}
总结:
- 一旦经历了
var的操作,所得出的属性,window,这种属性叫做不可配置的属性。(通过var给window增加的属性叫做不可配置属性) - 不可配置的属性,
delete删不掉
try catch
try{}catch{}finally{}
Error.name6种错误信息
EvalError:eval()的使用与定义不一致。RanageError:数据越界。ReferenceError:非法或不能识别的引用数值。SyntaxError:发生语法解析错误。TypeError:操作数类型错误。URLError:URL处理函数使用不当。