js的学习
js作为客户端语言
按照相关的js语法,去操作页面中的元素,有时还要操作浏览器里面的一些功能
- ECMAScript :js的语法规范(变量,数据类型,操作语句)
- DOM(doucment object model)文档对象模型,提供一些js的属性和方法,用来操作页面中的dom元素
- BOM(brower object model)浏览器对象模型,提供一些js的属性和方法,用来操作浏览器的
js中变量 variable
variable:变量,在编程语言中,变量就是一个名字用来存储和代表不同的值
// es3 创建一个变量
var a = 12;
// es6 创建一个变量
let b =12;
const c =12; // constant创建的变量存储的值是不能被修改的,所以基本上constant创建的可以理解为常量。(但是它和常量有些区别)
// 创建函数也相当于创建一个变量
function(){}
//创建类也相当于创建变量
class{}
// es6的模块导入也相当于创建一个变量
import xxx from './xxx'
// Symbol 创建唯一值
let n =Symbol(0)
let m = Symbol(0) // m!=n
js中的命名规范
- 严格区分大小写
- 使用数字,字母,下划线,$ ,数字不能作为开头
let $box; //一般jquery获取的变量用$开头、
let _box; //一般公共变量用_开头
let 1box; //不能使用数字开头命名变量
- 使用用驼峰命名法:单个字母可以全小写,多余两个单词的话,首字母小写,其余每一个有意义的单词首字母大写(命名尽可能语义法,使用英文单词)
let a; let studentName; let studentInfo;
// 了解一些常用的变量缩写:add delete update create get等一些
// 不正确的写法(汉语拼音命名,汉语英语混合)
let xinXi; let xueshengXinXi;
- 不能使用关键字和保留字命名
当下有特殊含义的是关键字(class var let const 等)
未来可能成为关键字的是保留字
js中常用的数据类型
- 基本数据类型
- 数字 number
常规数字和NaN - 字符串 string
- 布尔 bool
- 空对象指针 null
- 未定义 undefined
- 数字 number
- 引用数据类型
- 对象数据类型object
- {} 普通对象
- [] 数组对象
- 正则 /1/
- Math数学函数对象
- 日期对象
- 函数数据类型function
- 对象数据类型object
number数字类型
包含常规数字和NaN,NAN不是一个数字,但是他属于数字类型
- NaN和任何值都不想等
- isNaN() 检测一个值是不是非有效数字,如果不是有效数字的话,返回true,否则返回false,
isNaN()的检测机制首先验证检测的值是否为数字类型,如果不是先基于Number()这个方法,把值转换为数字类型
isNaN('10') 会返回false,意思就是‘10’是一个有效数字
使用Number()把非数字类型转换为数字
- 把字符串转换为数字,只要字符串中有一个非有效数字字符串(第一个点除外),结果都是NaN
Number('123.5') // 123.5 Number('123.5px') // NaN Number('12.3.3') // NaN Number('') // 0
- 把bool类型转化为数字类型
Number(false) // = 0 Number(true) // = 1 // 所以我们在使用isNaN()判断false 和 true 的时候返回的都是false,就是说true和false是有效数字
- null 转换为数字类型
Number(null) // 0
- undefined转换为数字类型
Number(undefined) // = NaN
- 把引用数据类型转换为数字的时候,是先把它基于toString()方法转化为字符串,然后再转换为数字
Number({}) //nan 先通过toString()转为object,然后再转为数字就是NaN Number({name:'10'}) //nan 先通过toString()转为object,然后再转为数字就是NaN Number([]) // 0 先通过toString()转为"" 然后再转为数字就是0 Number([12]) // 12 先通过toString()转为'12' 然后再转为数字就是12 Number([1,2]) //NaN
使用parseInt() parseFloat()把其他类型的值转换为数字类型,
parseInt/parseFloat([val],[进制])
:对于字符串来说,他是从左到右依次查找有效数字字符,直到遇到非有效数字字符,停止查找(不管后面是否还有数字,都不找了)- parseInt()与Number()的区别 :Number(true)= 1, parseInt(true) = NaN
parseInt机制就是先判断传进来的是不是数字,是的话,转化,不是的话,判断是不是字符串,是的话转换,不是字符串的先使用tostring()转为字符串
我们使用 == 的时候可能出现把字符串转换为数字
string字符串数据类型
所有用单引号,双引号,反引号包起来的都是字符串
使用toString把非字符串转换为字符串
- 使用toString()方法把非字符串转化为字符串就是在给变量添加一个引号,但是object类型的数据除外
// 我们在使用toString()的时候不是直接12.toString()的,基本类型值都不能直接xx.toString()这样调 var a = 12; a.toString(); 或者 (12).toString() // 注意: null 和 undefined 虽然可以使用toString()转为字符串,但是会报错 // 对象使用toString()的时候,会返回[object Object] ({}).toString() // "[object Object]" 因为Object.prototype.toString()方法不是转为字符串,它是用来检测数据类型的
字符串拼接
- 四则运算中,加法可能会存在字符串拼接,加法中一旦遇到字符串就进行字符串拼接
字符串方法
- 基本类型字符串本身应该没有方法的和其他属性的,但是我们却能使用字符串的许多方法,这是因为有
基本包装类型这一说法
,我们为什么可以调用字符串的方法,就是因为在底层中,通过基本包装类型,处理后,就能使用方法了 - chatAt
- chatCodeAt
- concat() 连接两个或多个字符串:
- trim() 方法删除字符串两端的空白符:
- substr(n,m) 字符串截取 从索引n开始,截取m个字符,
不支持索引为负数
- substring(n,m) 字符串截取方法,从索引n截取到索引m,
不包含m
,不支持索引为负数 - slice(n,m) 字符串截取方法,从索引n截取到索引m,
不包含m
,可支持索引为负数 - indexOf 方法返回字符串中指定文本首次出现的索引(位置
- lastIndexOf 方法返回字符串中指定文本最后一次出现的索引(位置
- includes
- toUpperCase 把字符串转换为大写:
- toLowerCase 把字符串转换为小写:
- split(分隔符)将字符串转换为数组,与数组中的jion方法相反
- replace() 方法用另一个值替换在字符串中指定的值,默认地,replace() 只替换首个匹配
boolean布尔数据类型
只有 0, NaN , “” , null,undefined, 五个值转换为false,其余都转化为true
Boolean("")为false Boolean(" ") 为true,它们之前的区别就是有没有空格
- 其他类型转为布尔类型的方法
- Boolean()
- !// !!
- ! (取反:先转为boolean再取反)
- !! (取反之后再取反的意思,就是相当于使用Boolean() )
null 和undefined
- null表示"没有对象",即该处不应该有值。典型用法是:
- 作为函数的参数,表示该函数的参数不是对象。
- 作为对象原型链的终点。
- undefined表示"缺少值",就是此处应该有一个值,但是还没有定义。典型用法是:
- 变量被声明了,但没有赋值时,就等于undefined。
- 调用函数时,应该提供的参数没有提供,该参数等于undefined。
- 对象没有赋值的属性,该属性的值为undefined。
- 函数没有返回值时,默认返回undefined。
引用数据类型
object对象数据类型
- 对象中用数字作为属性值的情况下,我们要使用它,我们不能通过
person.1
这种方法,我们需要person[1]
,我们也可以使用person['name']
调用name属性 - 对象类型中的真删除和假删除
let person = { name:'zhangsan', age:10, 1:100 } //我们在想要删除对象中的属性的时候,我们可以使用person.name =null 或者是person.name=undefined这种假删除,就是属性值没了 但是属性名还在 // 真删除 delete person.name 就是真删除,属性名没有了 然后属性值也没
数组对象
- 数组是特殊的对象数据类型
- 数组的检测,
我们用typeof检测array类型的时候返回的是object,我们用instanceof检测数组的时候,会碰到不同的框架中有不同的构造函数,所以我们在es5中新增了Array.isArray()这个方法判断是否为数组
- array对象的方法
这些方法调用的时候都是数组对象调用
toString()
把数组转换为字符串,并返回结果。valueOf()
返回数组对象的原始值toLocaleString()
把数组转换为本地数组,并返回结果。jion()
把数组的所有元素放入一个字符串。元素通过指定的分隔符进行分隔。并不改变原数组
pop()
删除并返回数组的最后一个元素改变原数组
push()
向数组的末尾添加一个或更多元素,并返回新的数组长度。改变原数组
var a = [1,2,3,4] a.push(1,2,3) // 添加多个元素的情况下,返回添加后的数组长度(改变原数组)
shift()
删除并返回数组的第一个元素改变原数组
unshift()
向数组的开头添加一个或更多元素,并返回新的长度改变原数组
sort()
对数组的元素进行升序排序得到一个新数组
- 官方解释:sort() 方法用原地算法对数组的元素进行排序,并返回数组,默认排序顺序是在将元素转换为字符串,然后比较它们的UTF-16代码单元值序列时构建的。
- arr.sort([compareFunction]),他有一个函数参数compareFunction(第一个用于比较的元素。,第二个用于比较的元素。)
- 如果 compareFunction(a, b) 小于 0 ,那么 a 会被排列到 b 之前;
- 如果 compareFunction(a, b) 等于 0 , a 和 b 的相对位置不变
- 如果 compareFunction(a, b) 大于 0 , b 会被排列到 a 之前
// sort 方法如果不传递参数的话,默认是根据字符串Unicode码点排序的,就是大于10的话,会按照字符串第一个字符的大小排序 var a = [1,2,23,3,4,24,21,31] a.sort() // [1, 2, 21, 23, 24, 3, 31, 4] // 如果处理大于10的数字的排序的话我们用下面放 a.sort((a,b)=>{ return a-b; //从小到大 return b-a; // 从大到小 })
reverse()
颠倒数组中元素的顺序。得到一个新数组
concat()
连接两个或更多的数组,并返回结果创建新数组,原数组不变
slice()
从某个已有的数组返回选定的元素,返回结果创建一个新数组
splice()
对数组进行删除,插入,替换,改变原数组
,返回值是删除的元素(如果没有删除的话,返回空数组)
splice(n,m) // 两个参数表示从索引n开始删除m个,如果m不写的话 删除到末尾 splice(n,m,values) // 三个参数表示,两个参数表示从索引n开始删除m个元素,并用values占用。 splice(n,0,values) // 把values插入到数组索引n位置
indexOf
(查找项,起始位置) 从前往后,返回首次出现位置 -1没找到// 它与includes的区别: 如果数组中有NaN. 则 indexOf无法匹配到. 而includes可以匹配到.
lastIndexOf
(查找项,起始位置) 从后往前,返回首次出现位置 -1没找到every
(function(){},作用于对象(选填))给数组的每一项运行函数,若每一项都为true,则返回true
some
(function(){},作用于对象(选填))给数组的每一项运行函数,若任意一项为true,则返回true
filter(function(){},作用于对象(选填))
给数组的每一项运行函数,返回执行函数后为true的项组成的数组`forEach(function(){},作用于对象(选填))
给数组的每一项运行函数,没有返回值
map
(function(){},作用于对象(选填))给数组的每一项运行函数,返回每次函数调用的结果组成的数组
reduce()
方法接收一个函数作为累加器,数组中的每个值(从左到右)开始缩减,最终计算为一个值var arr = [1,2,3,4,5] var sum = arr.reduce(function(pre,cur,index,array){ return pre+cur },pre) //pre累加初始值
educeRight()
方法接收一个函数作为累加器,数组中的每个值(从右到左)开始缩减,最终计算为一个值
- ES6中的新增数组方法
find()
找出第一个符合条件的元素,然后返回,如果没有返回undefined 参数是一个函数,find方法的回调函数可以接受三个参数,依次为当前的值、当前的位置和原数组。[1, 5, 10, 15].find(function(value, index, arr) { return value > 9; }) // 10
findIndex()
与上面的find作用相同,它找到的是索引值,如果没有返回-1fill()
fill方法使用给定值,填充一个数组。entries()
keys()
values()
includes()
表示某个数组是否包含给定的值.返回一个bool值
作用与indexof一样,它们的区别就是indexof它内部使用严格相等运算符(===)进行判断,这会导致对NaN的误判。
Date对象
关注一下时间处理的方法
- 我们使用new Date()的时候,获取的是客服端本地的时间,这个时间是用户自己可以修改的,它得到的是一个对象
- getDay()获取一周中的星期几 0-6代表周日到周六
Math对象
- Math提供了一些数学公式和方法
- max 确定一组数值中的最大值
- min 确定一组数值中的最小值
var a = (1,2,3,4) Math.max(a) // 返回4 var b = [1,2,3,4] Math.max(b) //NaN 这样是不行的,Math.max只能传入多个数字参数,如果max中的变量是一个数组,那就相当于只有一个参数,这个参数是一个数组,先把数组使用Number()得到number类型的数字,在进行max方法 Math.max.apply(Math,b) // 解决max传递一个数组后是使用max Math.max(...b) // 解决max传递一个数组后是使用max
- Math.ceil() 向上舍入(主要有小数,那就整数加一)
- Math.floor() 向下舍入(舍去小数部分,取整数)
- Math.round() 执行标准舍入 (四舍五入)
注意正数的时候.5是进,负数的时候.5是舍去
- Math.random() 返回大于等于0小于1的一个随机数
Math.floor(Math.random() * (max - min + 1) ) + min; //获取一个min~max之间的随机整数(包含max) Math.floor(Math.random() * (max - min) ) + min;//获取一个min~max之间的随机整数(不包含max)
- Math.abs(num) 返回num的绝对值
- Math.sqrt(num) 返回num的平方根
RegExp类型(正则)
关注一下正则匹配规则
Function
- 函数声明和函数表达式有所区别,代码执行之前,会存在一个变量提升的过程。
- js函数没有重载(方法重载通常用于创建完成一组任务相似但参数的类型或参数的个数不同的方法,在js中传多少参数,与函数本身没有关系)
- 函数中的参数,如果是对象的时候,那么传递进来的就是对象的值(值传递,不同于引用传递)
- 函数的内部属性
arguments 和 this
// arguments 它是一个类数组(伪数组?),它包含着传入函数的所有参数,除了传入的参数之外,它还有一个callee这个属性,这个属性是一个指针,指向拥有这个arguments 对象的函数本身 // arguments.callee.caller,指向当前函数的引用者 // 当我们定义阶层函数的时候,或者是递归函数的时候,我们可以使用arguments 这个属性
- arguments:函数的内置实参集合
- 类数组集合,集合中存储这所有的函数执行时,传递的参数信息
- 不论是否设置形参,arguments都会存在
- 不论是否传递实参,arguments也都存在
- arguments的callee:存储的是当前函数本身(一般不用,js严格模式下禁止使用这些属性)
箭头函数
- 箭头函数中没有arguments这一个属性,如果我们想要在箭头函数中,获取所有的参数,我们可以通过下面这个方式
var sum = (...arg) =>{ console.log(arg) }
基本包装类型(属于引用类型)
String()
Boolean()
Number()
有了基本包装类型,所以我们的基本类型值都可以被当作对象来访问,以上三种基本包装类型
操作基本类型值的语句一经执行完毕,就回立即销毁新创建的包装对象
数据的存储方式
- 基本数据类型的存储
- 创建变量,并把它放在当前栈内存的变量存储区域
- 创建一个值,并把它放在当前栈内存的值存储空间中
- = 赋值的过程 就是让变量和值相互关联的过程
- 引用数据类型的存储
- 在内存中开辟空间(堆内存),变量存储空间(栈内存上) ,存放堆地址的空间(栈上)
- 把对象上的键值对依次存储到堆内存上
- 把栈上的变量和栈上的堆地址关联起来
js中数据类型检测
-
typeof :用来检测数据类型的运算符(注意这是一个运算符不是一个方法),它是检测
number,string,boolean,function,undefined,bigint,symbol
的最佳的工具typeof 1 // 返回"number" typeof undefined // "undefined" typeof 'xx' //返回"string" typeof null // 返回的是"object" typeof的局限性\ typeof function(){} // 返回 "function" //注意下面两种方式 typeof false // boolean var a = new Boolean(false) typeof a // object 注意typeof后面的变量 是通过构造函数实例出来的对象,还是普通的基本类型 let a = BigInt(111) typeof BigInt(111) // "bigint" let a = Symbol(1) typeof Symbol(a) // "symbol"
- typeof 返回的值是字符串类型的,
两个typeof运算返回的一定是"string"
- typeof 检测null返回
Object
,是因为null是一个空对象指针(在Javascript中二进制前三位都为0的话会被判断为Object类型,null的二进制表示全为0,自然前三位也是0,所以执行typeof时会返回"object"。
) - typeof检测对象的时候,不能检测到具体的对象类型(
object
array
正则
) - 通过构造函数创建出来的数据,哪怕是基础数据类型,返回值也是
object
typeof new String(111) // 返回值是 object typeof new Number(1) // 返回值是object
- typeof 返回的值是字符串类型的,
-
instanceof 用来检测当前实例是否属于某个类
- 用法: 当我们想要知道某个对象变量是具体什么对象的时候,我们使用它,
person instanceof Object
,它会返回true和false,使用instanceof
判断基本数据类型的时候,始终会返回false - 它的缺点:1.
基础数据类型不能通过该方式判断
(但是通过构造函数创建的除外
),2.如果网页中包含多个框架,那实际上就存在两个以上不同的全局执行环境,从而存在两个以上不同版本的构造函数。如果你从一个框架向另一个框架传入一个数组,那么传入的数组与在第二个框架中原生创建的数组分别具有各自不同的构造函数。1 instanceof Number // false new Number(1) instanceof Number // true // 第二种缺点情况 var iframe = document.createElement('iframe'); document.body.appendChild(iframe); xArray = window.frames[0].Array; var arr = new xArray(1,2,3); // [1,2,3] arr instanceof Array; // false
instanceof
的具体实现
//虽然在检测基本数据类型时 typeof 是非常得力的助手,但在检测引用类型的值时,这个操作符的 //用处不大。通常,我们并不是想知道某个值是对象,而是想知道它是什么类型的对象。为此ECMAScript提供了istanceof 操作符 // instance的具体实现如下 function instance(left,right){ let prototype = right.prototype; //获取类型的原型 let proto = left.__proto__; //获取对象的原型 while(true){ //通过while(true)死循环,进行循环体内的遍历,直到有返回值结束循环 //循环判断对象的原型是否等于类型的原型,直到对象原型为null,因为原型链最终为null if (proto === null || proto === undefined){ return false; } if (proto === prototype){ return true; } proto = proto.__proto__; } }
- 用法: 当我们想要知道某个对象变量是具体什么对象的时候,我们使用它,
-
constructor
查看对象对应的构造函数,constructor
在其对应对象的原型下面,是自动生成的。当我们写一个构造函数的时候,程序会自动添加:构造函数名.prototype.constructor = 构造函数名
- 使用:
实例.constructor = 构造函数名
返回值为true/false
- 缺点: 类在继承时会出现问题
function A(){}; function B(){}; A.prototype = new B(); //A继承自B var aObj = new A(); alert(aobj.constructor === B) -----------> true; alert(aobj.constructor === A) -----------> false;
- 使用:
-
object.prototype.toString.call() 检测数据类型最好的方法
- 对于
Object.prototype.toString()
方法,会返回一个形如"[object XXX]"
的字符串。如果对象的 toString() 方法未被重写,就会返回如上面形式的字符串。 - 如果对象原型上的
toString()
方法被重写的时候,那么就执行重写后的toString()
- 原理:
- 对于 Object.prototype.toString.call(arg),若参数为 null 或 undefined,直接返回结果。
Object.prototype.toString.call(null); // => "[object Null]" Object.prototype.toString.call(undefined); // => "[object Undefined]"
- 若参数不为 null 或 undefined,则将参数转为对象,再作判断。对于原始类型,转为对象的方法即装箱,此处不赘述。
- 转为对象后,取得该对象的 [Symbol.toStringTag] 属性值(可能会遍历原型链)作为 tag,如无该属性,或该属性值不为字符串类型,则依下表取得 tag, 然后返回 "[object " + tag + “]” 形式的字符串。
// Boolean 类型,tag 为 "Boolean" Object.prototype.toString.call(true); // => "[object Boolean]" // Number 类型,tag 为 "Number" Object.prototype.toString.call(1); // => "[object Boolean]" // String 类型,tag 为 "String" Object.prototype.toString.call(""); // => "[object String]" // Array 类型,tag 为 "String" Object.prototype.toString.call([]); // => "[object Array]" // Arguments 类型,tag 为 "Arguments" Object.prototype.toString.call((function() { return arguments; })()); // => "[object Arguments]" // Function 类型, tag 为 "Function" Object.prototype.toString.call(function(){}); // => "[object Function]" // Error 类型(包含子类型),tag 为 "Error" Object.prototype.toString.call(new Error()); // => "[object Error]" // RegExp 类型,tag 为 "RegExp" Object.prototype.toString.call(/\d+/); // => "[object RegExp]" // Date 类型,tag 为 "Date" Object.prototype.toString.call(new Date()); // => "[object Date]" // 其他类型,tag 为 "Object" Object.prototype.toString.call(new class {}); // => "[object Object]"
- 对于
一些经典的练习题
// 练习关于数据类型的存储方式
let a = {
n=1;
}
let b = a;
a.x = a = {
n:2
}
console.log(a.x) // undefined
console.log(b) // b = {n:1;x{n:2}}
// 练习基本类型转换
var a = 'abc' + 123 + 456; // 这里变成了字符串拼接 ‘abc123456’
var b = '456' - '123'; // 333
var c = 100 + true + 21.2 + null + undefined + 'tencent' + [] + null + 9 + false; //"NaNtencentnull9false"
console.log(a,b,c)
// 关于NaN的一些运算
var str = 'abc123'
var num = parseInt(str);
if(num == NaN){ //NaN不等于任何数
alert(NaN)
} else if(num ==123){
alert(123)
} else if(typeof num =='number'){
alert('number')
} else {
alert('str')
} // 结果是number
// 下面能输出"1"的是
alert(1) //对的 输出字符1
console.log(parseInt(1.3)) // 输出数字1
console.log(1) // 输出数字1
console.log(isNaN(1)) // 输出false
console.log(parseInt("1"))
// 下面结果是”undefined“的是
console.log(alert(1)) // “undefined” 因为alert()这个方法没有返回值,默认返回undefined,注意这里不是字符串“undefined”
typeof undefined // ”undefined“
console.log(parseInt(undefined)) // NaN
isNaN(undefined) // true
// 数组去重的思路
1. 创建一个新数组,然后循环原来数组,然后和新数组比较,判断是否存在,如果存在则不添加,不存在就添加(只是得到一个新的数组,并没有对原来数组去重)
//这种方法不能删除重复 {},
var a = [1,2,23,3,4,24,21,2,3,4,23,1,31]
var b = []
a.forEach(item=>{
if(b.includes(item)) return
b.push(item)
})
2. 循环数组中的每一项,每一次拿出来的值和它前面的值就行比较,比较过程中遇到一项和自己相同的就删除 (会存在数组塌陷问题)
// 下面这种方法不能去除{},NaN的重复问题
var a = [1,2,23,3,4,24,21,2,3,4,23,1,31]
for(var i =0;i<a.length;i++){
var item = a[i]
for(var j=i+1;j<a.length;j++){
var item2 = a[j];
if(item===item2){
a.splice(j,1);
j--; // 这句代码解决数组塌陷问题(我们删除数组中的一项时,数组整体前移一位,但是索引加一,这样会造成,有一位我们没有操作,所以当我们删除的时候,我们的索引不能加一,所以我们先加1,然后循环加一,这样就不增不减了)
}
}
}
3. 第二种方法的改版,只循环一次
var a = [1,2,23,3,4,24,21,2,3,4,23,1,31]
var obj = {} // 创建一个对象
for(let i =0;i<a.length;i++){
let item = a[i] //拿到循环的元素
if(obj[item]!==undefined){ // 判断是不是存在类如 obj[1] =1 的属性存在,如果存在的话,把数组中的删除
a.spliec(i,1)
i--;
continue;
}
obj[item] = item;// 不存在的话,新增属性到对象,对象的属性名和属性值一致,都是a[i]
}
4. function unique(ary){ // 此方法也是不能出去重复的{}和NaN
let obj = {}
for(let i =0;i<ary.length;i++){
let item = ary[i];
if(obj[item]!==undefined){
ary[i] = ary[ary.length-1];
ary.length--;
i--;
continue;
}
obj[item] = item
}
return ary;
}
js中的加减乘除本应是数学运算,但是如果遇到不是数字类型的值,需要基于Number把其转为数字,再进行运算,但是在+法过程中,如果遇到字符串的话就就行字符串拼接,就不再做四则运算了
下一篇(js的再学习(二))
+- ↩︎