关于js语句中分号的问题
一般语句下,可以在句尾省略分号:但是下面两种情况除外(可在行首加分号):
原型以及原型链图解
原型
原型
是JavaScript中一个比较难理解的概念,原型相关的属性也比较多,实例对象
有__proto__
属性,该属性指向构造函数的原型对象,函数对象有prototype
属性,该属性指向函数对象的原型对象,原型对象有constructor
属性,该属性指向构造函数。var p = new Person ( ) 的过程如下:
1. var p = { } ; 初始化一个对象p。
3. p. _proto_= Person . prototype; ,将对象p的 __proto__ 属性设置为 Person . prototype
4. Person . call ( p, ”张三”, 20 ) ; 调用构造函数Person来初始化p,主要执行constructor构造方法, 初始化属性, 并且绑定this
5. 返回新对象
` ` `
显示原型prototype
以及隐式原型__proto__
函数对象不仅有__proto__(隐式原型)
还有prototype显示原型
实例对象只有__proto__
隐式原型 原型链其实 是对象的隐式原型链,用来查找属性值
变量提升以及函数提升
变量提升
通过var
定义(声明)的变量,在定义语句之前就可以访问到,只不过值是undefined
var a = 3
function fu ( ) {
console. log ( a)
var a = 4
}
function fu ( ) {
var a;
console. log ( a)
a = 4
}
函数声明
提升
通过function声明式
生产的函数,在之前就可以直接调用(和变量提升一样,会在代码块的顶部就把函数给声明了) fn ( )
function fn ( ) {
console. log ( 'fn' )
}
fn2 ( )
var fn2 = function ( ) {
}
变量提升以及函数提升是如何产生的
在js中js引擎会优先解析var变量
和function定义
!在预解析完成后从上到下逐步进行! 解析var变量
时,会把值存储在执行环境
中,而不会去赋值,值是存储作用!例如: alert(a); var a = 2; 这时会输出undifiend,意思是没有被初始化没有被赋值! 在解析声明式function
时会把函数整体定义,这也就解释了为什么在function定义函数时为什么可以先调用后声明
了!其实表面上看是先调用了,其实在内部机制中第一步实行的是把以function方式定义的函数先声明了(预处理) 函数提升
和变量提升
的先后
问题
预处理阶段,当函数名
与变量名
重名时,优先保留函数
] 练习function a ( ) { }
var a;
console. log ( typeof a )
/ -- -- -- /
if ( ! ( b in window) ) {
var b = 1
}
console. log ( b)
/ -- -- -- /
var c = 1
function c ( c ) {
console. log ( c)
}
c ( 2 )
执行环境上下文
每次当控制器转到可执行代码
的时候,就会进入一个执行上下文
。执行上下文
是用于跟踪代码的运行情况
。执行上下文可以理解为当前代码的执行环境
,是在代码执行时
产生的。 JavaScript中的运行环境大概包括三种情况
代码在执行过程中会形成一个执行上下文栈
全局执行上下文(以及this的关系,执行环境上下文不等同于this) this 的值
是通过当前执行上下文中保存的作用域(对象)
来获取到的
在执行全局代码
前将window
确定为全局执行上下文 对全局数据进行预处理
var 定义的全局变量==》undefined添加为window属性 function声明的全局函数==》赋值fun,添加为window的方法 this=》赋值(window)
函数执行上下文(函数调用时产生)
在调用函数,准备执行函数体之前,创建对应的函数执行上下文对象 对局部数据进行预处理
形参变量=》赋值(实参)=》添加为执行上下文的属性 arguments==》赋值(实参列表),添加为执行上下文的属性 var 定义的局部变量==》undefined,添加执行上下文的属性 function声明的函数=》fun,添加为执行上下文的方法 this=》赋值(调用函数的对象)
执行上下文栈
在全局代码执行前,js引擎会创建一个栈来存储管理所有的执行上下文对象 在全局执行上下文(window)确定后,将其添加到栈中 在函数执行上下文创建后,将其添加到栈中(压栈) 当前函数执行完成后,将栈顶的对象移出(出栈) 当所有的代码执行完毕后,栈中只剩下window
执行上下文的练习
console. log ( 'glabl' , i)
var i = 1
foo ( i)
function foo ( i ) {
if ( i== 4 ) {
return
}
console. log ( 'start' , i)
foo ( i+ 1 )
console. log ( 'end' , i)
}
console. log ( 'globl' , i)
作用域以及作用域链
作用域以及执行环境上下文的关系
JavaScript属于解释型语言,JavaScript的执行分为:解释和执行两个阶段,这两个阶段所做的事并不一样
JavaScript解析阶段
便会确定作用域规则
,因此作用域在函数定义时就已经确定了,而不是在函数调用时确定,但是执行上下文是函数执行
之前创建
的。执行上下文最明显的就是this的指向
是执行时确定
的。而作用域访问的变量
是编写代码的结构
确定的。 执行上下文在运行时确定,随时可能改变
;作用域在定义时就确定,并且不会改变
一个作用域下可能包含若干个上下文环境
。有可能从来没有过上下文环境
(函数从来就没有被调用过);有可能有过,现在函数被调用完毕后,上下文环境被销毁了;有可能同时存在一个或多个(闭包)。同一个作用域下,不同的调用会产生不同的执行上下文环境,继而产生不同的变量的值。
闭包
什么是闭包
理解1:包含嵌套的内部函数 理解2:包含被引用变量(函数)的对象 如何产生闭包
常见的闭包
将函数作为另一个函数的返回值 function f1 ( ) {
var a= 2
function f2 ( ) {
a++
console. log ( a)
}
return fn2
}
var f = f1 ( )
f ( )
f ( )
f = null
将函数作为实参传递给另一个函数调用 function showDealy ( msg, time ) {
setTimeout ( function ( ) {
alert ( msg)
} , time)
}
showDelay ( '哈哈' ,2000 )
闭包的作用(作用)
使函数内部的变量在函数执行完之后,仍然存在内存中(延长了局部变量的生命周期) 让函数外部可以操作 到内部的数据 使用闭包定义JS模块
function myModule ( ) {
var msg = 'hhh'
function fn1 ( ) {
console. log ( 111 , msg)
}
function fn2 ( ) {
console. log ( 222 , msg)
}
return {
fn2 : fn2,
fn2 : fn1
}
}
( function ( windwo ) {
var msg = 'hhh'
function fn1 ( ) {
console. log ( 111 , msg)
}
function fn2 ( ) {
console. log ( 222 , msg)
}
window. myModule = {
fn2 : fn2,
fn2 : fn1
}
} ) ( windwo)
闭包的缺点
函数执行完,函数内部的局部变量没有释放,占用的内存时间会变长
容易造成内存泄漏
缺点的解决方式:能不用就不用,及时释放
内存溢出以及内存泄漏
内存溢出
一种程序运行出现的错误 当程序运行需要的内存超过了剩余的内存时,就会抛出内存溢出的错误 内存泄漏
内存泄漏: 占用的内存没有及时释放
内存泄漏
积累多了
就会容易导致内存溢出
常见的内存泄漏: 意外的全局变量
,没有及时清理
的定时器
以及回调函数
,没有及时释放
的闭包
关于闭包的练习
var name = 'window'
var object = {
name : 'object' ,
getName : function ( ) {
return function ( ) {
return this . name
}
}
}
object. getName ( ) ( )
var name = 'window'
var object = {
name : 'object' ,
getName : function ( ) {
var that = this
return function ( ) {
return that. name
}
}
}
object. getName ( ) ( )
对象
创建对象的不同方式
Object构造函数 var p= new Object ( ) ;
p. name = 'Nike' ;
p. age = 29 ;
字面量
工厂模式
创建方式function createPerson ( name, age, job ) {
var o = new Object ( ) ;
o. name = name;
o. age = age;
o. job = job;
o. sayName = function ( ) {
alert ( this . name) ;
} ;
return o;
}
var person1 = createPerson ( 'Nike' , 29 , 'teacher' ) ;
var person2 = createPerson ( 'Arvin' , 20 , 'student' ) ;
缺点: 通过工厂函数创建时,返回的是一个对象。那么我们就无法判断返回的对象究竟是一个什么样的类型 自定义构造函数方式
创建方式如下:相比较于工厂模式,通过该模式可以识别的对象的类型
function Person ( name, age, job ) {
this . name = name;
this . age = age;
this . job = job;
this . sayName = function ( ) {
alert ( this . name) ;
} ;
}
var person1 = new Person ( 'Nike' , 29 , 'teacher' ) ;
var person2 = new Person ( 'Arvin' , 20 , 'student' ) ;
缺点:每个方法
都要在每个实例上重新创建一遍
,方法指的就是我们在对象里面定义的函数。如果方法的数量很多,就会占用很多不必要的内存 原型的方式
自定义构造函数+原型的方式
创建方式function Person ( name, age, job ) {
this . name = name;
this . age = age;
this . job = job;
}
Person . prototype = {
constructor : Person,
sayName : function ( ) {
alert ( this . name) ;
} ;
}
var person1 = new Person ( 'Nike' , 20 , 'teacher' ) ;
通过构造函数方式创建属性
,然后配合原型创建方法
相结合的方式。就能解决原型模式以及构造函数模式的不足
继承的几种模式
原型链继承:让子类
的原型对象
作为父类的实例
继承父类方法,主要代码Son.prototype = new Parent();Son.prototype.constructor = Son
function Person ( name, age ) {
this . name = name
this . age = age
}
Person . prototype. sayName = function ( name ) {
console. log ( name)
}
function Student ( price ) {
this . price = price
}
Student . prototype = new Person ( )
Student . prototype. constructor = Student
Student . prototype. sayPrice = function ( price ) {
console. log ( price)
}
var stu1 = new Student ( )
var stu2 = new Student ( )
stu1. sayName ( )
stu1. name = 'name'
stu2. name = 'name2'
构造函数继承,通过在子类
中改变
子类实例的this
指向为父类,继承父类的属性
function Person ( name, age ) {
this . name = name
this . age = age
}
function Student ( name, age. price ) {
Person . call ( this , name, age)
this . price = price
}
var stu = new Student ( '222' , '2222' , 2222 )
console. log ( stu. name, stu. age, stu. price)
组合继承(原型链实现对父类型对象方法的继承,利用构造函数初始化相同属性)
function Person ( name, age ) {
this . name = name
this . age = age
}
Person . prototype. sayName = function ( name ) {
console. log ( name)
}
function Student ( name, age. price ) {
Person . call ( this , name, age)
this . price = price
}
Student . prototype = new Person ( )
Student . prototype. constructor = Student
Student . prototype. sayPrice = function ( price ) {
console. log ( price)
}
原型式继承
function object ( obj ) {
function F ( ) { }
F . prototype = obj;
return new F ( ) ;
}
var person = {
name : "Nicholas" ,
friends : [ "Shelby" , "Court" , "Van" ]
} ;
var anotherPerson = object ( person) ;
anotherPerson. name = "Greg" ;
anotherPerson. friends. push ( "Rob" ) ;
var yetAnotherPerson = object ( person) ;
yetAnotherPerson. name = "Linda" ;
yetAnotherPerson. friends. push ( "Barbie" ) ;
alert ( person. friends) ;
寄生式继承:在原型式继承的基础上,增强对象,返回构造函数
function createAnother ( original ) {
var clone = object ( original) ;
clone. sayHi = function ( ) {
alert ( "hi" ) ;
} ;
return clone;
}
寄生组合式继承:结合借用构造函数传递参数和寄生模式实现继承
function inheritPrototype ( subType, superType ) {
var prototype = Object. create ( superType. prototype) ;
prototype. constructor = subType;
subType. prototype = prototype;
}
function SuperType ( name ) {
this . name = name;
this . colors = [ "red" , "blue" , "green" ] ;
}
SuperType . prototype. sayName = function ( ) {
alert ( this . name) ;
} ;
function SubType ( name, age ) {
SuperType . call ( this , name) ;
this . age = age;
}
inheritPrototype ( SubType, SuperType) ;
SubType . prototype. sayAge = function ( ) {
alert ( this . age) ;
}
var instance1 = new SubType ( "xyc" , 23 ) ;
var instance2 = new SubType ( "lxy" , 23 ) ;
instance1. colors. push ( "2" ) ;
instance1. colors. push ( "3" ) ;
混入方式继承多个对象 ES6 class方式继承
进程以及线程
浏览器内核
浏览器内核:支撑浏览器运行的最核心的程序 不同的浏览器可能内核不一样
chrome ,safari :webkit firefox: gecko ie : trident 360,搜狗等 :trident, webkit 内核有很多模块组成
js引擎模块:负责js程序的编译与运行 html/css文档解析模块:负责页面文本的解析 DOM/CSS模块:扶着dom/css在内存中的相关处理 布局和渲染模块:负责页面的布局和效果的绘制 。。。 定时器模块:负责定时器的管理 事件响应模块:负责事件的管理 网络请求模块: 负责ajax模块
定时器真是定时执行的吗
定时器真是定时执行的吗
定时器并不能保证真正的定时执行 一般会延迟一点,意外情况下会延迟很久,主要情况要看事件循环机制的 定时器回调函数是在分线程执行的吗
定时器是如何实现的
事件循环机制
H5中的web workers