关于js
什么是js?
概念 : JavaScript 是一种运行在 客户端 的 脚本语言
语言 :
- 编译语言:Java/C/C++/OC/Swift... 运行代码之前,把所有的代码先编译一遍,再运行每一行代码(先编译找语法错误,然后再一行一行执行里面的逻辑)
- 脚本语言:JavaScript/PHP/Python... 运行一行解析一行,不需要提前编译 , 非编译语言
javascript的组成
- ECMAScript (标准) JavaScript的核心,描述了语言的基本语法和数据类型,ECMAScript是一套标准,定义了一种语言的标准,与具体实现无关
- DOM:一套操作网页元素的API
- BOM:一套操作浏览器功能的API
输出语句 (5种)
alert : 警告框
//alert会弹出一个警告框 alert("hello world");
confirm : 确认框
//confirm弹出一个确定框 confirm("hello word");
prompt : 输入框
//prompt:弹出一个输入框,可以输入值 prompt("hellow");
document.write : 网页中写入内容
//可以识别标签 document.write("hello world"); document.write("<h1>hello world</h1>");
控制台输出
//F12打开控制台,在console中可以看到打印的信息 console.log("hello word");
变量的命名规则和规范
规则 : ( 必须遵守的,不遵守会报错 )
-
由字母、数字、下划线、$符号组成 ;
-
不能以数字开头
-
区分大小写
-
不能是关键字和保留字 (不用死记 , 慢慢就见多了)
//关键字:对于js来说有特殊意义的一些单词
//保留字:js保留了一些单词,这些单词现在不用,但是以后要用。
关键字:
保留字:
交换两个变量的值
var temp = a; a = b; b = temp;
不使用临时变量,交换两个数值变量的值
var a = a+b; b = a-b; a = a-b;
数据类型
javascript中数据类型分为简单数据类型和复杂数据类型
简单数据类型(基本数据类型)
number、string、boolean、undefined、null
数值 字符串 布尔 声明未赋值 空类型
复杂数据类型:
函数 function
数组 Array
对象 Object
关于进制
在javascript中表示一个数字,除了有我们常用的十进制11, 22,33
等,还可以使用八进制、十六进制表示等。
- 八进制
-
// 八进制 0-7 逢八进一 0开头的 var num = 010; // 10 = 0 * 8 ^0 + 1 * 8^1 = 8 var num1 = 0123; // 123 = 3 * 8^0 + 2 * 8^1 + 1*8^2 = 3+16+64 = 83 console.log(num1);
- 十六进制
-
// 十六进制 0-15 (0-9 a b c d e f ) 逢十六进一 0x开头 var num1 = 0x10; // 10 = 0*16^0 + 1 * 16^1 = 16; var num2 = 0x131; // 131 = 1*16^0 + 3*16^1 + 1*16^2 = 1 + 48 + 256 = 305; console.log(num2);
关于二进制,计算机在只认识二进制,所以所有的代码最终都会转换成二进制数据。
浮点数
浮点数就是小数,,比如0.1
- 浮点数精度丢失问题
-
var num1 = 0.1; // 大部分出现在奇数 var num2 = 0.2; console.log(num1 + num2); //0.30000000000000004 // 解决 : 后面学到的 往上取整,,往下取整 四舍五入 // 转化为整数进行计算
- 科学计数法
//当一次数字很大的时候,可以用科学计数法来表示
var num = 5e+5; //5乘以10的5次方
var num = 3e-3;//3乘以10的-3次方
数值范围
最小值:Number.MIN_VALUE,这个值为: 5e-324 最大值:Number.MAX_VALUE,这个值为: 1.7976931348623157e+308 无穷大:Infinity 1/0 无穷小:-Infinity
关于 typeof
function fn () { } var ary = [1,2,3] console.log( typeof 1) // number console.log( typeof 'a') // string console.log( typeof true) // boolean console.log( typeof undefined); // undefined console.log( typeof null); // object console.log( typeof fn); //function console.log( typeof Array); //function console.log( typeof Object); //function console.log( typeof ary); //object
字符串类型
字符串类型,使用双引号 "
或者 '
包裹起来的字符
转义字符
运算符优先级
()
的优先级最高- 一元运算符(++, --, !)
- 算术运算符(先
*/%
, 后+-
) - 比较运算符 (先
> < >= <=
, 后== === != !==
) - 逻辑运算符(先
&&
后||
)
// 转化为 布尔类型 true/false //1. 构造函数 Boolean() var age = 'abc'; console.log( Boolean(age) ); // true //1. 记忆那些 转化为false // 没有值的 => false // 非正常值 => false //2. 五大数据类型 // 数字类型 0 // 字符串 '' // undefined // null // NaN console.log(Boolean(0)); //false console.log(Boolean('')); //false console.log(Boolean(undefined)); //false console.log(Boolean(null)); //false console.log(Boolean(NaN)); //false // 注意点 : // console.log( Boolean('false') ); //2. !! 取两次非 console.log(!!123); console.log( Boolean(123) ); // 总结 //1. 转化为数字 //1.1 构造函数 Number(); //1.2 方法 : parseInt(值) parseFloat() //1.3 + -0 //2. 转化为字符串 //2.1 构造函数 String() //2.2 值.toString() //2.3 + '' //3. 转化为布尔 //3.1 Boolean() // false // 0 '' undefined null NaN //3.2 !! 取两次非
switch语句
switch (变量) { case 值1: 语句1; break; case 值2: 语句2; break; case 值3: 语句3; break; … default: 默认语句; break; }
break可以省略,如果省略,代码会继续执行下一个case
switch 语句在比较值时使用的是全等操作符, 因此不会发生类型转换(例如,字符串'10' 不等于数值 10)
switch (num) { case 0: console.log('星期天'); break; case 1: console.log('星期一'); break; case 2: console.log('星期二'); break; case 3: console.log('星期三'); break; case 4: console.log('星期四'); break; case 5: console.log('星期五'); break; case 6: console.log('星期六'); break; default: console.log('你输入的有误'); break; }
while循环
//当循环条件为true时,执行循环体, //当循环条件为false时,结束循环。 while(循环条件){ //循环体:需要循环执行的语句 }
//1. 打印1-100之间所有的数 //2. 计算1-100之间所有数的和 // 初始化变量 var i = 1; var sum = 0; while(i <= 100){//判断条件 sum += i;//循环体 i++;//自增,修改循环条件(不能省略) } console.log(sum);
do..while循环
do..while循环和while循环非常像,二者经常可以相互替代,但是do..while的特点是不管条件成不成立,都会执行一次。
do { //循环体; }while(条件)
//初始化变量 var i = 1; var sum = 0; do{ sum += i;//循环体 i++;//自增 }while(i <= 100);//循环条件
创建数组 方法
// 创建数组 //1.1 创建空数组 以后用构造函数创建的对象 ( new Array() ) var arr1 = new Array(); console.log(arr1); // [] //1.2 创建一个有内容的数组 var arr2 = new Array(1,4,7); console.log(arr2); // [1,4,7] //1.3 Arrar() 里面放一个值,代表的是创建一个长度为4的空数组 var arr3 = new Array(4); console.log(arr3); // [empty × 4]
//2. 字面量 直接量: 一眼就能看出是什么类型 // 是我们最常用的方式 简单粗暴 //2.1 创建一个空数组 var arr1 = []; console.log(arr1); //2.2 创建一个有内容的数组 var arr2 = ['zs','ls']; console.log(arr2); //2.3 也来尝试一下一个值 var arr3 = [40]; console.log(arr3); // [40]
如何存储?
存储基本类型存储的是值,存储引用类型存储的是地址
数据类型
数字: 整数 小数
字符串: " " ' '
布尔:true false
null
undefined
对象 { } 数组 函数 伪数组 元素
操作符
短路与&&
console.log( 1&&2 ) // 2 第一个满足条件,第二个也满足条件,输出第二个
console.log( 1&&0 ) // 0 第一个满足条件,输出第二个
console.log( 0&&1 ) // 0 第一个0已经是false了,后边的就不会看了 所以直接输出 0
短路或 ||
console.log( 1||2 ) // 第一个已经满足条件,就不看后边的了,直接输出 1
console.log( 0||1 ) // 第一个不满足条件,输出第二个
console.log( 0|| ' ' ) // 输出 ' '
console.log( !1 ) //false
console.log( !!1 ) //true
console.log( +true ) // 输出 1
console.log( +false ) //输出 0
console.log( +' ' ) // 0
什么时候是加的逻辑?什么时候是拼接字符串?
如果 + 号只有一个操作数,那么 就是把 操作数 转换为 数字类型 比如 console.log( +true ) // 输出 1
如果 + 号有两个操作数,并且其中有一个是字符串,那么一定是拼接字符串 如 console.log( 1+'false' ) //输出 1false
console.log( Number('1.1111') ) // 1.1111
console.log( Number(false) ) // 0
console.log( Number(null) ) // 0
console.log( Number(undefined) ) // NaN
js的内置对象
js内置对象就是指JavaScript自带的一些对象,供开发者使用,这些对提供了一些常用的功能
常见的内置对象有Math Sting Array Date() 等
Math对象
数组中常用的方法:
数组.concat( 另一个数组 ) 把两个数组合并成一个
join( ) 把数组转化成字符串
split() 按照指定的字符将字符串分隔为数组
join('-') 将数组中的元素按照指定的字符分隔转换为字符串,默认是逗号。 (split() 按照指定的字符将字符串分隔为数组 字符的方法 )
slice(开始下标,结束下标) 包含头,不包含尾 原来的数组没有发生变化,返回一个新的数组
splice(开始下标,截取的长度,删除后补充的元素) 返回一个新的数组,原理的数组发生了变化
push() 从后边加
pop() 从后边删
unshift() 从前边加
shift() 从前边删
var result = arr.forEach(function(elem,index,self){// elem 是数组中的每一项元素 index是数组的每一项下标 self 当前遍历的数组
依次遍历数组中的元素,将数组中的元素执行相同的操作,不会改变原数组
});
var result = arr.filter(function(elem,index,self){ // filter 过滤
return 判断条件
})
var result = every(function(elem,index,self){
return 如果数组中每一项都符合要求就返回true,否则返回false
})
var result = some(function(){
return 只要有一些符合要求,就返回true
})
var result = arr.map(function(elem,index,self){
return 遍历数组中的每一项元素,返回一个新的数组
})
作用域
代码一旦书写完毕,那么作用域就形成了,跟调用函数没有关系
外部作用域不能访问函数作用域的变量,函数作用域内部可以访问外部
访问变量的时候,先在自己的作用域中查找,如果找不到就沿着作用域链往上找,直到全局
如果只是访问变量,如果找到全局都没有的话会报错, xx is not undefined
如果要给变量赋值,找到全局都没有的话,浏览器会帮我们自动帮我们创建这个变量
给变量赋值之前,或者访问变量之前,要先找到变量
如果代码执行之前,要考虑预解析规则,函数的代码执行之前,也要执行预解析规则
<script> // 知识点 xx in window window中有没有xx,如果有返回true,如果没有false var a; if ( 'b' in window){ //console.log( 'b' in window ) //false 在window中找不到'b'所以 是false console.log(111) var a = 10 console.log(a) } alert(a) // undefined </script>
<script> function fn1(){ var a = b = c = 1; // var a = 1 // b = 1 // c = 1 } fn1(); console.log(c); // 1 console.log(b); // 1 console.log(a); // a is not defined </script>
<script> var a = 1; function fn () { var a = 2; function fnSon (a) { a = 3; console.log( a ) // 3 } fnSon(); console.log( a ) // 2 } console.log( a ) // 1 fn() console.log( a ) // 1 // 输出结果 : 1 3 2 1 </script>
<script> var a = 1; function fn () { var a = 2; function fnSon () { a = 3; console.log( a ) // 3 } fnSon(); console.log( a ) // 3 } console.log( a ) // 1 fn() console.log( a ) // 1 // 输出结果 : 1 3 3 1 </script>
<script> var a = 1; function a () { a++ } console.log(a) // 1 </script>
<script> // 解题知识点:在给变量赋值之前要先找到变量(记住了 a 和 a.x的位置) var a = { x : 1 } var b = a a.x = a = { n : 1 } console.log( a.x ) // undefined console.log( b.x ) //{ n : 1 } </script>
对象查找属性的规则:
先在自己身上找,如果有,直接使用 ,如果没有,顺着原型链往上找,找到了就使用,找不到就继续往上找,如果找到了Null都没有的话,就返回undefined;
外部作用域不能访问内部作用域,内部作用域可以访问外部
查找变量,先在当前作用域查找,如果没有会沿着作用域链往上找
给变量赋值,访问的时候,要先找到变量
在代码执行前要先考虑预解析规则,函数里的代码执行前也要考虑预解析规则
查找变量,如果找到全局都没有会报错。
如果给变量赋值没有声明,找到全局都没有声明,会自动在全局创建这个变量
在js中函数会构成一个局部作用域。全局中有函数,全局的是0级链,全局中的函数时1级链,函数中如果有函数就是2级链,这样的关系就是作用域链。
作用域链是由作用域串联而成的链式结构
什么是原型?
原型指的是原型对象,prototype。函数创建出来的时候,对应产生一个对象,这个对象叫做原型对象。 除了箭头函数以外,所有函数都默认有原型对象,通过函数名.prototype的方式可以访问
原型对象prototype有什么作用?
节约内存。比如构造函数中的方法,放到prototype中可以避免重复创建
数组和对象 中的很多方法也是放到prototype中的,如果没有prototype,数组和对象的一堆方法,会很浪费内存。将数组和对象的方法放到各自的原型对象prototype中,数组和对象
的实例,就可以直接使用。(实例可以默认访问自己函数的原型对象,自己函数原型对象中的方法,可以直接使用)
原型对象的属性?
原型对象中有一个 constructor 的属性, 指向自己的函数
原型对象默认可以被自己的函数实例访问
什么是实例?
通过构造函数new出来的对象就叫做实例
被构造函数new出来的实例可以通过._ _proto_ _ 来访问对应的原型对象
什么是原型链?
实例---null构成的链式结构,由原型对象逐级继承 与 实例形成的链式结构 就是原型链
原型链上保存着这个对象所有可用的方法
构造函数new出来的实例对象,查找规则?
先在自己身上找,如果没有,沿着原型链往上找,如果找到null还没有,就返回undefined
内置对象
var ary = new Array( ); // 说明 ary 是 Array的实例
通过new Array() 创建了一个新的对象ary。这个ary是Array的实例
var ary = [ ] // 底层也是 new Array( )
结论:所有的数组都是Array的实例,因为所有的数组都是Array的实例,所以所有的数组都可以访问到Array的原型对象中的方法
<!-- 在原型对象中添加方法,简单写法 -->
<!-- 在原型对象中添加方法,简单写法 --> <script> function fn(){ } fn.prototype = { constructor:fn, // 如果使用简单方式给原型添加方法,会丢失constructor,需要手动添加 one : function(){ console.log(1); }, two : function(){ console.log(2); }, three : function(){ console.log(3); } } var f = new fn(); f.one(); f.two(); f.three(); </script>
一个实例一旦创建出来,它的原型链就固定了,不会随着函数原型的改变而改变。这就是原型链的不可变
当我们需要自己调用函数,并且要修改函数中this的指向的时候,用 call / apply
当我们不需要自己调用函数,要浏览器帮我们调用(事件处理函数,定时器的回调函数),并且要修改函数中this的指向的时候,用bind。bind不会调用方法,会克隆
bind的注意点:
function fn ( ) {
console.log( this )
}
var f = fn.bind([1,2,3])
fn() // [1,2,3]
为什么要学习继承?简单点说是为了少写代码
单个对象的继承
方法一: 单个对象的继承可以用: for key in xxx 和 对象.hasOwnProperty('属性名') //如果对象有这个属性就返回true,否则返回false
方法二
单个对象继承,利用 var 新对象 = Object.create(被继承的对象)
<!-- 单个对象的继承 --> <script> var zh = { name:'zh', age:18, money:9999999999999 } // 利用Object.create() 实现继承 var zxh = Object.create(zh) zxh.name = 'zhx' console.log(zxh.age); // 18 console.log(zxh) </script>
构造函数的继承(借用构造函数法)
<!-- 构造函数的继承 --> <script> // 借用构造函数法实现 function Person(name,age) { this.name = name; this.age = age; } function Student(name,age,score) { Person.call(this,name,age) this.score = score } // Student里面的name和age不想再重复的书写了,借用构造函数法实现继承 var zh = new Student( 'zh',18,100) console.log( zh ) // {name: "zh", age: 18, score: 100} </script>
<!-- 构造函数的继承 原型继承-->
<!-- 构造函数的继承 --> <script> // 借用构造函数法实现 function Person(name,age) { this.name = name; this.age = age; } Person.prototype.say = function () { console.log( '你好,我是'+this.name ); } function Student(name,age,score) { Person.call(this,name,age) //借用构造函数继承,只能继承写在函数体内的this.xxx的属性 this.score = score } // 如果想要继续原型上的属性和方法 ,那么就要使用原型继承这个方式 // 原型继承:专门用来继承写在原型上的属性和方法 Student.prototype = new Person() var zh = new Student( 'zh',18,100) console.log( zh.say() ) // 你好,我是zh </script>
私有的属性写在构造函数中,公有的属性写在原型上
借用构造函数法用来继承私有属性,原型继承用来继承公有的属性
组合继承 = 解构构造函数继承 + 原型继承
Function自己创造了自己
Object是大写Function的实例
在js中,先有的Function函数,通过 new Function() 来创建对象。
javaScript是一门单线程的语言
JS在工作的时候,一次只能做一件事,做完一件事, 才能去做下一件事情。这就是单线程语言
java是多线程语言,一次可以做好几件事情
浏览器中的 渲染引擎: 渲染html+css
js解析器:执行js代码
v8引擎:google创建出来的,是js工作的平台,js是在v8引擎中工作的
堆和栈可以理解为内存中的一块区域
堆里面存储着很多数据,比如 声明的变量,声明的对象 都是存在堆里面的
堆: 变量,对象
基本数据类型存在栈里
引用数据类型存在堆里
js中,所有代码执行的时候,都会加载到栈中执行
事件轮询一直在观察栈和任务队列,如果栈空了,就会去任务队列中拿第一个出来,然后放到栈中执行
面向对象
面向过程的思想:一步一步的具体去做这个事情
面向对象的思想:需要哪个对象,调用这个对象的某个方法,帮助我们实现具体的需求
面向对象是对面向过程的封装
什么是面向对象?
面向对象是一种编程思想,js本身就是基于面向对象构建出来的(例如:js中有很多内置类 ,像 promise就是ES6中新增的一个内置类,我们可以基于new Promise来创建一个实例,来管理异步编程),包括我们平时用的
Vue jq 等也是基于面向对象构建出来的,他们都是类,平时开发的时候都是创建他们的实例来操作的。
js中的面向对象,和其它编程语言还是有略微不同的,js中类和实例是基于原型和原型链机制来处理的,而且js中关于类的重载 重写 继承和其它语言也不太一样
arguments是函数中的一个对象,arguments是一个伪数组,里面存储了所有传进来的实参
继承:子类继承父类中的属性和方法(目的是让子类的实例能够调取父类中的属性和方法)
方案一:原型继承
让父类中的属性和方法在子类实例的原型链上
Son.prototyp = new Father( )
Son.protoype.constructor = Son
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta http-equiv="X-UA-Compatible" content="ie=edge"> <title>Document</title> </head> <body> <script> function Father (x) { this.x = x; } Father.prototype.getX = function () { console.log( this.x ) } function Son (y) { this.y = y; Father.call(this,y) } Son.prototype = new Father(200) Son.protoype.constructor = Son // 保证原型重定向后的完整性 Son.prototype.getY = function () { console.log( this.y ) } let son1 = new Son(100) son1.y; son1.getY(); </script> </body> </html>