【前端】【ES5】【JS的基本使用】
一、前概
首先我们需要明白js是一门解释型(解释一行执行一行)面向对象的编程语言,在这个前提下,我们再继续下面的文档阅读。
面向对象是一种思想,目前主流实现这种思想的有两种方式,一种是类继承,一种是实现继承,js就是通过原型链进行的实现继承。
结构:
1.面向对象分类
2.变量
3.js的执行过程
4.作用域和作用域链
5.闭包与立即执行函数
6.原型和原型链
7.继承原型和原型链
7.继承面向对象设计
注:"use strict"; 使用严格模式
二、变量
2.1、变量分类
2.1.1、基本类型
Number var num =1;
String var str = "qw";
Boolean var bool = true;
null
undefined
2.1.2、引用类型
2.1.2.1、包装类
1、示例
Number()、String()、Boolean()
通过这三种构造函数创建包装类
var num = new Number(1);
var str = new String("test");
var bool = new Boolean(true);
2、原始类型如何使用方法的过程
//此时str是原始类型
var str = "ase";
// var str = new String(str) 将str转化为临时的包装对象,这样就可以获取length属性,
str.length = 1;
//调用结束后,此对象销毁
//继续,重新执行此操作: var str = new String(str) 将str转化为临时的包装对象,这个对象是一个新的包装对象,所以length还是3
console.log(str.length) //3
//var str = new String(str) 给str这个临时对象添加属性len,赋值为2
str.len = 2;
//调用完毕,销毁这个临时对象
//var str = new String(str) 获取len属性 ,但这个新的对象里面没有len属性,所以值为undefined
console.log(str.len) //undefined
//调用完毕,销毁该临时对象
2.1.2.2、其他
Object
Array
Function
Date
RegExp
引用类型都是对象,函数也是对象,他们的原型最终都是Object的实例
function Person(){}
var obj = new Person();
var p = typeof Person //typeof 对函数做了特殊处理,本质上也是对象
var t1 = obj instanceof Person
var t2 = obj instanceof Object
console.log(obj,p,t1,t2)
2.2、变量的传值
基本类型 按值传递
引用类型 共享传递(本质上也是按值传递,只不过这个值是对象的引用地址)
三种传值策略:
--按值传递(call by value): 函数的形参是被调用时所传实参的副本。修改形参的值并不会影响实参。
--按引用传递(call by reference):传参时直接将对象的地址传入进去(不像共享传递是copy的对象地址值的副本),当函数内部发生变化时,外部的对象也会跟着变化;正式的说,函数的形参接收实参的隐式引用,而不再是副本。
--共享传递(call by sharing):调用函数传参时,函数接受对象实参引用的副本(既不是按值传递的对象副本,也不是按引用传递的隐式引用)
扩展:通过共享调用(传递一个可以随后变异的对象)来实现类似的效果,在Java,Python和Ruby等语言中使用。
2.2.1、基本类型的值传递
var a = b = 1;
b = 2;
console.log(a)//1
#解释
在第一行的语句里,首先b被赋值为1;然后b将他的值复制了一份给a,所以b值的变化于a无影响
2.2.2、引用类型的共享传递
//第一种 全局
var a = b = {x:1};
b.x = 3;
console.log(a.x)//3
#解释
首先在第一行,b被赋值{x:1}的引用地址值,然后,b将他的值(这个值是{x:1}的引用值)复制了一份给a,此时,a与b同时指向了{x:1}对象,所以当b修改x的值时,a输出x时,也会变成3。
//第二种 函数
//1.
var obj = {
x: 1
};
function test(o) {
o.x = 2;
}
test(obj)
console.log(obj.x)//2
//2.
var obj = {
x: 1
};
function test(o) {
o = new Object();
o.x =4;
}
test(obj)
console.log(obj.x)//1
#解释
写这两个例子是想说明共享传递和引用传递的区别,如果js是引用传递的话,那么第二个obj的属性x应该是4。
三、JS执行过程
全局对象(GO对象 global object):
全局对象,可以理解为window对象
运行期上下文(AO对象 activation object):
当函数执行时,会创建一个称为执行期上下文的内部对象。一个执行期上下文定义了一个函数执行时的环境,*函数每次执行时对应的执行上下文都是独一无二的*,所以多次调用一个函数会导致创建多个执行上下文,当函数执行完毕,它所产生的执行上下文被销毁。
****查找变量:从作用域链的顶端依次向下查找。
js执行三部曲
#语法分析
#预编译
#解释执行
一步一步执行,有赋值语句的,替换GO对象里的对应属性名的(在函数里就替换AO对象里的)
预编译
#全局预编译(代码执行的前一刻)
1.创建GO对象(执行期上下文)
2.寻找形参和变量声明 将变量和形参名作为GO对象的属性名,值为undefined
3.函数声明 并将函数体赋给 GO里的函数声明
函数预编译(函数执行的前一刻)
1.创建AO对象(执行期上下文)
2.寻找形参和变量声明 将变量和形参名作为AO对象的属性名,值为undefined
3.将实参值和形参相统一 将实参的值赋予形参,将AO对象里的值替换
4.函数声明 并将函数体赋给 AO里的函数声明
(在函数里面找变量的值,会先从AO对象里面去找,如果没有就去GO对象里面去找)
流程示例
var b = 11;
function a(a,b){
console.log("内1"+a)//function a
a =10;
console.log("内1"+b)//undefined
var a = b = 23;
function a(){
}
function c(){}
console.log("内2"+a) //23
console.log("内2"+b) //23
console.log("内1"+c) //function c
var c;
}
console.log("外1"+a);//function a
a(1);
var a = 30;
console.log("外2"+a); //30
console.log("外"+b); //11
#解释
1.创建GO对象 Global Object
2.变量声明
GO{
b:undefined
}
3.函数声明
GO{
b:undefined
a:function a(a,b){....}
}
#####解释到这一行 console.log("外1"+a);######
#####var b =11######
GO{
b:11
a:function a(a,b){....}
}
#####a(1)######函数预编译
4.函数预编译,创建AO对象 Activation Object
5.变量声明和形参
AO{
a:undefiend
b:undefiend
c:undefined
}
6.形参和实参统一
AO{
a:1
b:undefiend
c:undefined
}
7.函数声明
AO{
a:function a(){}
b:undefiend
c:function c(){}
}
8.解释执行
#####console.log("外1"+a);######
#####a=10;######
AO{
a:10
b:undefiend
c:function c(){}
}
#####console.log("内1"+b)######
##### var a = b = 23;######
GO{
b:11
a:function a(a,b){....}
}
AO{
a:23
b:23
c:function c(){}
}
##### console.log("内2"+a) console.log("内2"+b) console.log("内1"+c)#####
------------至此,函数执行完毕----------
#####var a =10 ######
GO{
b:11
a:30
}
#####console.log("外2"+a); console.log("外"+b);
四、作用域和作用域链
4.1、定义
函数有个属性,叫[[scope]],这个属性只有浏览器引擎可以使用,这个就是作用域,里面存储着执行期上下文的集合,通过链式连接,这个链就是作用域链。每个执行期上下文都包含着this、arguments和document,这里的this在非严格模式下,表示windows.但是,GO对象里面只有window和document
4.2、解释
代码:
function a(){
var aa =1;
function b(){
var bb =2;
fucntion c(){
var cc =3;
}
c()
}
b()
}
a()
var glob = 100;
流程示意:
a defined a. [[scope] ] -- > 0 : G0
a doing a. [[scope]] -- > 0 : aA0
1 : G0,
b defined b. [ [scope]] -- > 0 : aA0
1 : G0
b doing b.[ [scope]] -- > 0 : bAO
1 :aA0
2 : G0,
c defined c.[[scope]] --> 0 : bA0
1 : aA0
2 : G0
c doing c.[ [scope]] -- > 0 : cA0
1 : bA0,
2 : aA0,
3 : G0
4.3、图片类似示意
解释:
b在定义的时候,是在a的作用域的基础上构建自己的作用域,所以b作用域里的aAo,和a里的AO是同一个,他们有着相同的引用
当a执行完毕后,就会去掉对自己的AO对象的引用,紧接着b也会去掉对自己的AO对象的引用和对a的AO对象的引用。当a的AO对象没有引用指向它时,就会被销毁。
五、闭包与立即执行函数
5.1、闭包
当外部函数调用对象(函数也是对象)的内部函数时,就叫做闭包,闭包会导致内存泄漏(内存不释放)
闭包的作用
实现公有变量 累加器
可以用作缓存(存储结构)
*****用于封装,属性私有化******
******模块化开发,防止污染全局变量******
代码:
function a(){
var num = 100;
function b(){
num++;
console.log(num)
}
return b;
}
var demo = a();
demo()//101
demo()//102
结构示意
a defined a. [[scope] ] -- > 0 : G0
a doing a. [[scope]] -- > 0 : aA0
1 : G0,
b defined b. [ [scope]] -- > 0 : aA0
1 : G0
b doing b.[ [scope]] -- > 0 : bAO
1 :aA0
2 : G0,
GO{
demo:undefined
a:{function}
}
a定义:aAO{
num:undefined
b:{function}
}
a执行与b定义 aAO{
num:100
b:{function}
}
b执行:bAO{
}
解释:
虽然a执行完毕,但是b被return出去并被赋予给demo,demo执行时就是执行b,这就导致了,a的AO对象不被释放;虽然
每执行一次demo,b都会创建自己新的AO对象,但是num值是在a的AO对象里的,而不管b执行多少遍,这些b引用的都是同一个aAo,所以执行一次,num的值就加1
5.2、立即执行函数
5.2.1、定义
针对初始化的函数 可以传参可以有返回值 与普通函数的区别就在于只执行一次就被销毁
(function(){}()) W3C推荐
(function (){})()
,逗号运算符 var a =(1-1,2-1);//1 如果有运算先算前面的,最后将后面的结果返回给a
5.2.2、解释
function test(){} //这个叫函数声明
var test = function(){} //函数表达式
//只有表达式才能被执行符号声明
test() //可以
function test(){}() //报错
test = function(){}() //可以
+function(){}()//可以
-function(){}()//可以
!function(){}()//可以
&&function(){}()//可以 注意前面要加点东西
||function(){}()//可以 注意前面要加点东西
现在再看看
(function (){})()
()括号的作用就是将里面函数变成表达式,使用完后销毁
5.2.3、示例
//闭包里运用立即执行函数
function test(){
var arr = [];
for(var i = 0; i < 10; i++){
(function(j){
arr[j] = function(){
console.log(j)
}
}(i))
}
return arr;
}
var test_arr = test();
for (var j = 0; j < test_arr.length; j++) {
// console.log(test_arr[j])
test_arr[j]()
}
//
<ul>
<li>1</li>
<li>2</li>
<li>3</li>
<li>4</li>
</ul>
<script type="text/javascript">
var liArr = document.getElementsByTagName('li');
for (var i = 0; i < liArr.length; i++) {
(function(j){
liArr[i].onclick = function(){
console.log(j)
}
}(i))
}
</script>
六、面向对象设计
6.1、对象分类
6.1.1、内部对象
Math Global 可以直接调用,在代码开始前,已实例化好
6.1.2、宿主对象
6.1.2.1、BOM对象
window对象
location对象
navigator对象
screen对象
history对象
6.1.2.2、DOM对象
请看第八章
6.1.3、自定义对象
6.1.3.1、创建对象的几种方式
#对象直接量
var obj = {};
#构造函数
function F(){}
var obj = new F();
#Object.create(原型)
6.1.3.2、构造函数原理(需要先理解原型)
function F(){}
var obj = new F();
在使用构造函数创建对象时,
第一步,构造函数会在一开始隐式的创建一个this对象:this-->{__proto__:F.prototype}
第二步,会调用this.xxx = xxxx
第三步,隐式的返回this //你也可以显式的返回this,返回其他的也可以,但是必须返回引用类型,如果返回是值类型或null、undefined,则仍然隐式返回this
示例
var obj = { name:"111s"}
Person.prototype = obj
function Person() {
//this-->{__proto__:obj}
console.log(this.__proto__)//会将obj打印出来
}
var p = new Person()
console.log(p)
6.2、原型和原型链
6.2.1、定义
函数对象有个属性叫prototype,这个就是原型;
function Grand(){
this.test ="test"
}
var grand = new Grand();
Father.prototype = grand;
Father.prototype.constructor = Father;//优化,将构造器重新指向Father
function Father(){
this.name = "father"
}
var father = new Father();
Son.prototype = father
Son.prototype.constructor = Son;//优化,将构造器重新指向Son
function Son(){
}
var son = new Son();
console.log(son)
这种链式调用,叫做原型链
6.2.2、call与apply使用
call和apply作用就是改变构造函数里的this指向,唯一的不同就是参数传递的方式的不同,call支持多个参数,apply只支持一个参数(数组形式)。
第一个参数传对象,可以为null、undefined(非严格模式),但这时this就变成了全局对象了(可以理解为window对象)。
var name = "外面"
var obj = {
name:"里面",
show:function (){
console.log(this.name)
},
show_strict:function (){
"use strict";
console.log(this.name)
}
}
obj.show.call(null)
obj.show.call()
obj.show_strict.call(null)//严格模式报错
obj.show_strict.call()//严格模式报错
6.3、继承模式
6.3.1、原型链
缺点:会继承无用的属性和方法
看原型定义里的示例,就是原型链
6.3.2、构造函数
function Person(name,age){
this.name = name ;
this.age = age;
}
function Student(name,age,grade){
Person.call(this,name,age)//节省了代码,但本质上并没有改善效率
this.grade = grade;
}
var stu1 = new Student("测试",11,11)
console.log(stu1)
6.3.3、共享原型
缺点:一旦需要在原型上增加属性和方法时,其他的也会拥有该属性和方法
function Person(){
this.last ="测试"
}
Teacher.prototype = new Person();
function Teacher(name,sex){
this.name = name;
this.sex = sex;
}
function Student(grade){
this.grade = grade;
}
Student.prototype = Teacher.prototype; //将Teacher的原型共享给Student
var stu1 = new Student(11)
var tea1 = new Teacher("哈哈",11)
// Student.prototype.show = function(){
// console.log(111)
// }
console.log(stu1,tea1)
示意图
解释:在Student的原型上增加了一个show的方法,结果Teacher也拥有了这个方法
6.3.4、圣杯模式
/*
圣杯模式
原型链+共享原型+构造函数
通过构造函数作为中间层,将父类的原型继承,在需要给自己的原型添加属性和方法时,又不会影响父类的原型
同时,将类的构造器从新指向自己的构造函数
并将真正继承的原型信息保留下来,以防以后需要
*/
function extend(Class, SuperClass) {
function F() {};
F.prototype = SuperClass.prototype;
Class.prototype = new F();
Class.constructor = Class;
Class.prototype.uber = SuperClass.prototype;//可以不写
}
//雅虎
//闭包的高级应用
var inherit = (function() {
var F = function() {};//使用闭包将变量F私有化
return function(Class, SuperClass) {
F.prototype = SuperClass.prototype;
Class.prototype = new F();
Class.constructor = Class;
Class.prototype.uber = SuperClass.prototype;
}
}())
示例:
function Father() {
}
Father.prototype.name = "测测";
function Son() {
}
extend(Son, Father)
var son = new Son();
Son.prototype.sex = '男';
var father = new Father();
console.log(son)
七、闭包的高级应用
7.1、封装,私有化变量
function Test() {
var name, sex;
this.getName = function() {//闭包 私有的作用域未释放
return name;
};
this.setName = function(value) {
name = value;
}
}
var obj = new Test();
obj.setName('哈哈');
var test = obj.getName()
console.log(test)
#解释
只能通过getName和setName查看和修改值,直接获取获取不到
7.2、模块化开发
由于企业开发项目一般都是有多人开发,一个html里可能会有十几个js文件,当这些js文件都被引入同一个html里时,就会出现很多问题,比如同名变量和方法,因为此时这些js都处于window全局对象里,所以当出现同名变量时,值就会发生变化。这时候就可以使用闭包来解决这种情况。
var initTest = (function (){
var name,sex;
function t1(){
name ="ceshi"
}
function t2(){
console.log(name)
}
return function(){
t1()
t2()
}
}())
initTest()//这样不管谁开发的代码,只要直接调用这个方法名就可以了
八、DOM
九、事件
9.1、事件的绑定与移除
9.1.1.绑定
#html onclick="在这里面写"
#on+type
ele.onxxx(function(){})
#addEventListener
obj.addEventListener(type,fn,boolean) type:事件类型 fn 匿名函数/函数名 boolean true:捕获 false:冒泡
#IE attachEvent
obj.attachEvent('on'+type,fn) //IE只有冒泡事件
9.1.2.解除
#on+type
ele.onxxx = null;
#addEventListener
obj.removeEventListener(type,fn,boolean) type:事件类型 fn 匿名函数/函数名 boolean true:捕获 false:
解除时,fn必须是绑定的函数名,即使是相同的匿名函数也不行
#IE detachEvent
obj.detachEvent('on'+type,fn)
9.2、事件模型
事件流:事件捕获阶段-->处于目标阶段-->事件冒泡阶段
前提:结构嵌套
先捕获在冒泡
9.2.1、捕获
父元素-->子元素
示例:当子元素触发点击事件时,会从父元素一层层向下,最后触发子元素的点击事件
9.2.2、冒泡
子元素-->父元素
示例:当子元素触发点击事件时,会一层层向上,触发父元素的点击事件
9.2.3、综合示例:
<div id="test1" style="width: 200px;height: 200px;background-color: red;">
<div id="test2" style="width: 100px;height: 100px;background-color: blue;">
<div id="test3" style="width: 50px;height: 50px;background-color: green;">
</div>
</div>
</div>
var div1 = document.getElementById('test1');
var div2 = document.getElementById('test2');
var div3 = document.getElementById('test3');
//捕获事件
div1.addEventListener('click',function(e){
console.log("catch_111")
},true)
div2.addEventListener('click',function(e){
console.log("catch_222")
},true)
div3.addEventListener('click',function(e){
console.log("catch_333")
},true)
//冒泡事件
div1.addEventListener('click',function(e){
console.log("doubble_111")
},false)
div2.addEventListener('click',function(e){
console.log("doubble_222")
},false)
div3.addEventListener('click',function(e){
console.log("doubble_333")
},false)
9.3、事件对象
其他:event
IE:window.event
事件源对象:原始操作元素对象,比如综合示例被点击的那个子元素
event.target 火狐
event.scrElement IE
谷歌都有
9.4、事件委托
利用事件冒泡对事件源对象(可以理解为被点击的子元素)进行操作
--html
<ul id="ul">
<li>1</li>
<li>2</li>
<li>3</li>
<li>4</li>
<li>5</li>
</ul>
--js
var ul = document.getElementById('ul');
ul.onclick = function(e){
var event = e || window.event
var target = event.target || event.srcElement
console.log(target.innerText)
}
9.5、取消冒泡和阻止默认事件
9.5.1、取消冒泡
IE(9)
e.cancelBubble = true
非IE
event.stopPropagation()
方法封装(在当前点击的元素方法;里调用)
function stopDoubble(e){
var event = e||window.event;
if(event.stopPropagation{
event.stopPropagation()
}else{
event.cancelBubble = false;
}
}
--html
<div id="test1" style="width: 200px;height: 200px;background-color: red;">
<div id="test2" style="width: 100px;height: 100px;background-color: blue;">
<div id="test3" style="width: 50px;height: 50px;background-color: green;">
</div>
</div>
</div>
--js
var div1 = document.getElementById('test1');
var div2 = document.getElementById('test2');
var div3 = document.getElementById('test3');
// 冒泡事件
div1.addEventListener('click',function(e){
console.log("doubble_111")
},false)
div2.addEventListener('click',function(e){
console.log("doubble_222")
},false)
div3.addEventListener('click',function(e){
stopDoubble(e)
console.log("doubble_333")
},false)
function stopDoubble(e){
var event = e||window.event;
if(event.stopPropagation){
event.stopPropagation()
}else{
event.cancelBubble = false;
}
}
9.5.2、取消默认事件
默认事件:表单提交 a标签跳转 右键菜单等
IE(9)
e.returnValue = true
非IE
event.preventDefault()
方法封装
function preventDefaults(e){
var event = e || window.event)
if(event.preventDefault){
event.preventDefault()
}else{
event.returnValue = false;
}
}
--html
<a href="http://www.baidu.com" id="test1">火狐</a>
--js
var a = document.getElementById('test1');
a.onclick = function(e){
preventDefaults(e)
console.log('测试跳转')
}
function preventDefaults(e){
// debugger
var event = e || window.event
if(event.preventDefault){
event.preventDefault()
}else{
event.returnValue = false;
}
}
9.6、事件类型
1.焦点事件
2.鼠标与滚轮事件
十、其他
10.1、对象枚举
for in
可枚举的
in
属性是否可调用
instanceof
是否是一条链上的
hasOwnProperty
是否是自己定义的(原型上不算)
10.2、arguments.callee func.caller
arguments.callee 自己的引用
func.caller 谁调用func
10.3、克隆
//深度克隆
function deepClone(origin, target) {
target = target || {};
var toStr = Object.prototype.toString;
var arrStr = "[object Array]";
for (var prop in origin) {
if (origin.hasOwnProperty(prop)) {
if (origin[prop] !=='null' && typeof(origin[prop]) == 'object') {
if (toStr.call(origin[prop]) == arrStr) {
target[prop] = [];
} else {
target[prop] = {};
}
deepClone(origin[prop], target[prop]);
} else {
target[prop] = origin[prop];
}
}
}
return target
}
10.4、函数执行和创建对象的区别
function test(){
//this = object.create(test.prototype)
a = 5;
alert(a);
alert(this.a);
var a;
alert(a)
}
test()
new test()
#解释:
test
AO{
a:5
this:window
}
new test()
AO{
a:5,
this:{} //构造函数的this隐式赋值 看上面代码里注释
}
加new操作符的变化,其实就只有this的指向发生改变
10.5、作用域
js没有块级作用域,所以不管是if还是for循环后面大括号里的变量都相当于在外面一层
if(true){
var name = "ss";
}
相当于:
var name;
if(true){
name = "ss";
}
函数外定义的变量拥有全局作用域
标签:function,ES5,console,log,对象,前端,JS,var,obj
From: https://www.cnblogs.com/simpletime/p/16768667.html