《一起学前端 之 JS 篇》是记录从 2020.4.29 开始,学习 JS 的收获与感悟。
运行在客户端的脚本语言,不是服务器端语言。
注意 prompt 返回的是字符串!
var name = prompt('请输入您的名字');
alert(name);
注意 name 虽然不是关键字,也不是保留字,但是一般在浏览器里面有特殊含义,所以最好不要使用它作为变量名。
JS把数据类型分类两类 基本数据类型( 简单数据类型 / 值类型 ) 和 复杂数据类型 (引用类型)。
其中简单数据类型包括:Number、String、Boolean、Undefined、Null。
复杂数据类型包括: object(自定义对象、内置对象、浏览器对象)
基本数据类型 在存储时,变量中存储的是值本身;而 复杂数据类型 在存储时,变量中存储的是地址(引用)。
还有一个 symbol
用方法 isNaN() 判断
var str="my name is andy";
console.log(str.length);//15
var myName='牛牛';
alert('hello' + ' ' + 'world'); //hello world
alert('100' + '100'); //100100
alert('11' + 12); //1112
alert(11 + 11) //22
alert('我的名字叫'+myName);//我的名字叫牛牛
alert('我的名字叫myName');//我的名字叫myName
指的是字符串里面的值不可变,虽然看上去内容可以改变,但其实地址变了,内存中新开辟了一个空间。
如果不断给一个字符串变量赋值,会占用很大的内存。
console.log(true+1);//2
console.log(false+1);//1
一个声明后没有被赋值的变量会有一个默认值 undefined
var variable;
console.log(variable);//undefined
console.log('你好'+variable);//你好undefined
console.log(11+variable);//NAN
一个变量声明后给nulll值,里面存的值为空
var vari =null;
console.log('你好'+vari);//你好null
console.log(11+vari);11
console.log(true+vari);//1
typeof 可以用来检测变量的数据类型
var name = '小蜜';
var age = 18;
console.log(typeof name);//string
console.log(typeof age);//number
console.log(parseInt('3.14'));//3 取整
console.log(parseInt('3.94'));//3 取整
console.log(parseInt('120px'));// 120
console.log(parseInt('rem120px'));// NaN
console.log(parseFloat('3.14'));//3.14
console.log(parseFloat('120px'));//120
console.log(parseFloat('rem120px'));//NaN
var str='123';
console.log(Number(str));
console.log(Number('12'));
console.log('12'-0);//12
console.log('123'-'120')//3
console.log('123'*1);//123
浮点数的最高精度是17位小数,但在进行算术计算时其精度远远不如整数,因为它会先转为二进制再计算。
console.log(0.1 + 0.2);//0.30000000000000004
console.log(0.07 * 100);//7.000000000000001
此外不要直接判断两个浮点数是否相等,容易出错。
var num = 0.3;
console.log(num == 0.3); //true
num = 0.1 + 0.2; //误差产生
console.log(num == 0.3); //false
注意前置与后置的区别,还有注意 表达式中同时出现一个变量的前置与后置递增 时的情况
var a = 10;
++a;//11
var b = ++a + 2;//++a 先对a自加1,再返回a
console.log(b); //14
var c = 10;
c++;//11
var d = c++ + 2;//c++ 先返回c,再对c进行自加1
console.log(d); //13
var e = 10;
//从左往右。先返回e,为10,然后对e进行自加1,得到11;先对e进行自加1,得到12,返回e;实际上等于 10+12=22
var f = e++ + ++e;
console.log(f); //22
注意 表达式num中的内容 需要跟 case 里面的内容全等(‘===’),才会进入该case执行代码。
var num = 1;
switch (num) {
case '1':
console.log(1);
break;
case '2':
console.log(2);
break;
case 1:
console.log('匹配数字1');
break;
default:
console.log('不匹配');
}
1 利用 new 创建数组
var arr = new Array(); //创建一个空数组
var arr1 = new Array(2); //创建一个长度为2的空数组
var arr2 = new Array(2, 3); //等价于数组[2,3]。创建一个数组,它有两个元素,分别为2和3
2 利用数组字面量创建数组
var arr1 = [];
//数组元素的类型不限
var arr2 = ['小白', 1, true, 28.4];
1 修改length长度
var arr = [1, 2, 3];
arr.length = 5;//把数组长度改为5
console.log(arr[3]);//undefined
console.log(arr[4]);//undefined
2 新增索引号,追加数组元素
var arr = [1, 2, 3];
arr[3] = 4;
console.log(arr.length); //4
console.log(arr); //1,2,3,4
应用:挑选一个数组中大于等于10的元素出来,放入一个新的数组中
//方法1 用一个计数变量
var arr = [2, 0, 6, 1, 77, 0, 52, 10, 25, 7];
var newArr = [];
var count=0;
for (var i = 0; i < arr.length; i++) {
if (arr[i] >= 10) {
newArr[count] = arr[i];
count++;
}
}
console.log(newArr);
//方法2 用 newArr.length 作为索引
var arr = [2, 0, 6, 1, 77, 0, 52, 10, 25, 7];
var newArr = [];
for (var i = 0; i < arr.length; i++) {
if (arr[i] >= 10) {
newArr[newArr.length] = arr[i];
}
}
console.log(newArr);
//方法3 push
var arr = [1, 2, 3];
arr.push(4);
console.log(arr); //[1,2,3,4]
arr.push(5, 6, 7);
console.log(arr); //[1,2,3,4,5,6,7]
console.log(arr.pop()); //7
arr.shift();
console.log(arr); //[2,3,4,5,6]
arr.unshift(0);
console.log(arr); //[0,2,3,4,5,6]
var arr = [1, 2, 3, 4, 5];
arr.reverse();
console.log(arr);//[5,4,3,2,1]
普通的 sort 会按位并按大小进行比较。
带有 比较函数 的sort能按照一定规律进行比较。
//普通 sort
var arr = [1, 2, 33, 32, 23];
console.log(arr.sort()); //[1,2,23,32,33]
//带有比较函数的sort
var arr1 = [1, 2, 33, 32, 23];
arr1.sort(function (a, b) {
// return a-b;//升序
return b - a; //降序
})
console.log(arr1); //[33, 32, 23, 2, 1]
可以使用 instanceof 或 Array.isArray()。instanceof 与 typeof 的区别是,typeof 最多只能判别变量为对象,但不能判别出具体是什么对象,所以需要用 instanceof 来判别。
var arr = [1, 2];
var num = 10;
console.log(arr instanceof Array); //true
console.log(Array.isArray(arr)); //true
console.log(num instanceof Array); //false
console.log(Array.isArray(num)); //false
获得数组中元素的索引
var arr = ['blue', 'pink', 'orange', 'white', 'pink'];
// 只返回最先匹配的那一个的位置
console.log(arr.indexOf('pink')); //1
//只返回最先匹配的那一个,但并不会改变索引的顺序
console.log(arr.lastIndexOf('pink')); //4
//如果元素不在该数组中 则返回 -1
console.log(arr.lastIndexOf('green')); //-1
应用案例:数组去重
var arr = [2, 3, 6, 7, 8, 2, 4, 9, 221, 3];
var newArr = [];
for (var i = 0; i < arr.length; i++) {
if (newArr.indexOf(arr[i]) == -1) {
newArr.push(arr[i]);
}
}
console.log(newArr); // [2, 3, 6, 7, 8, 4, 9, 221]
var arr = ['I', 'Love', 'You'];
console.log(arr.toString()); //I,Love,You
console.log(arr.join('-')); //I-Love-You
console.log(arr.join(' ')); //I Love You
function getSum(num1, num2) {
console.log(num1 + num2);
}
//如果实参形参个数匹配,则正常输出结果
getSum(1, 2); //3
//如果实参个数>形参个数,则会对号入座,直到取到形参个数为止
getSum(1, 2, 3); //3
//如果实参个数<形参个数,则多出的形参会定义为undefined,形参可以看做是不用声明的变量
getSum(1); //NaN
1 函数 return 只能返回一个值。如果 return 多个由逗号分隔的值,则只返回最后一个值。
function fn(num1, num2) {
return num1, num2;
}
console.log(fn(1, 2)); //2
2 如果没有 return 则返回 undefined
function fn(num1, num2) {
}
console.log(fn(1, 2)); //undefined
break 结束当前循环体
continue 跳出本次循环,继续执行当前循环体的下次循环
return 退出循环,并返回值
arguments 是 伪数组,它具有 length 属性,可按照 索引 的方式进行存储,但它没有真正数组的一些方法,比如 pop()、push()等。
function fn() {
console.log(arguments); //[1,2,3,4,5]
console.log(arguments.length); //5
console.log(arguments[2]); //3
}
fn(1, 2, 3, 4, 5);
应用:
function getMax() {
var max = arguments[0];
for (var i = 1; i < arguments.length; i++) {
if (arguments[i] > max) {
max = arguments[i];
}
}
return max;
}
console.log(getMax(1, 2, 3, 4, 6, 7, 8, 9, 111, 110)); //111
function fun() {
console.log("函数式声明");
}
var fun2 = function() {
console.log("函数表达式");
}
var fn = new Function('a', 'b', 'console.log(a+b)');
fn(1, 2);//3
1 在函数内部没有声明直接赋值的变量,属于全局变量(但是函数必须被调用,该变量才能创建)
2 函数的形参可以看做是局部变量
function fun(aru) {
var num1 = aru;
num2 = 20;
}
fun(29);//调用fun,才有全局变量num2
// console.log(aru); //error
console.log(num2); //20
内部函数访问外部变量时,采取链式查找的方式来决定取哪个值,类似于就近原则
var num = 10;
function fun1() {
var num = 20;
function fun2() {
console.log(num);//20
}
fun2();
}
fun1();
js引擎运行js分为两步:预解析 和 代码执行 。
预解析 分为 变量提升(变量预解析) 和 函数提升(函数预解析)。
变量提升,就是把所有的变量声明提升到当前作用域最前面,不提升赋值操作。
函数提升,就是把所有函数声明提升到当前作用域最前面,不调用函数。
预解析 时,js引擎会把js里面的所有 var 和 function 声明,提到当前作用域的最前面。
而 代码执行 时,按照代码书写顺序从上往下执行。
//案例1
console.log(num); //undefined
var num = 10;
//案例2
fn(); //11
function fn() {
console.log(11);
}
//案例3
fun(); //报错,fun is not a function
var fun = function () {
console.log(22);
}
//案例4
var num = 10;
fun();
function fun() {
console.log(num);
var num = 20;
}
//--------相当于执行了以下代码
var num;
function fun() {
var num;
console.log(num);
num = 20;
}
num = 10;
fun(); //undefined
属性 或 方法 采用 键值对 的形式,即 属性名: 属性值 。
使用对象属性有两种方法:
1 对象名.属性名
2 对象名['属性名']
删除对象属性的方法:
delete 对象名.属性名;
var obj = {
name: 'Json',
age: 18,
sex: 'man',
sayHi: function () {
console.log('hi');
}
}
var obj = new Object();
obj.name = '张三疯';
obj.age = '18';
obj.sex = '男';
obj.sayHi = function () {
console.log('hi~');
}
使用 构造函数 可以减少重复的代码
1 构造函数的首字母要大写
2 调用构造函数必须要用 new
3 构造函数的属性和方法前面必须添加 this
function Star(name, age, sex) {
this.name = name;
this.age = age;
this.sex = sex;
this.sing = function (song) {
console.log(song);
}
}
var Json = new Star('Json', 17, 'man');
console.log(Json.name); //Json
console.log(Json.age); //17
console.log(Json.sex); //man
Json.sing('happy'); //happy
new 在执行时会做四件事情:
1 在内存中创建一个新的空对象
2 让构造函数的this指向这个新对象
3 执行构造函数里面的代码,给这个新对象添加属性和方法
4 返回这个新对象(所以构造函数里面不需要 return )
语法 for(变量 in 对象){}
var obj = {
name: 'JSON',
age: 29,
sex: 'man',
fn: function () {
console.log('hello');
}
}
for (var k in obj) {
console.log(k);//k 输出得到的是属性名
console.log(obj[k]);//obj[K] 输出得到的是属性值
}
总结 :
for in 得到对象的key 或 数组、字符串的索引 ,
而 for of 和 forEach 一样,是直接得到值,但是 for of 不能对象用。
内置对象 是 js 自带的一些对象,供开发者使用,提供了一些常用的功能。比如 Math、Date、Array、String 等。
Math 对象不是构造函数,它具有数学常数和函数的属性与方法。
//绝对值方法
console.log(Math.abs(1)); //1
console.log(Math.abs(-1)); //1
console.log(Math.abs('-1')); //隐式转换 1
console.log(Math.abs('Json')); //NaN
//取整方法 floor 向下取整
console.log(Math.floor(1.1)); //1
console.log(Math.floor(1.9)); //1
//取整方法 ceil 向上取整
console.log(Math.ceil(1.1)); //2
console.log(Math.ceil(1.9)); //2
//取整方法 round 四舍五入取整 但是.5特殊,会往“大”的方向取整
console.log(Math.round(1.5)); //2
console.log(Math.round(1.1)); //1
console.log(Math.round(1.9)); //2
console.log(Math.round(-1.1)); //-1
console.log(Math.round(-1.5)); //-1
该函数返回一个伪随机浮点数,范围在 [ 0 ,1),即大于等于0,小于1(不包括1),我们可以将随机数的范围缩放到所需要的范围。
//返回一个 [ 0,1 ) 之间的随机数
function getRandom() {
return Math.random();
}
console.log(getRandom());
//返回一个 [min,max) 之间的随机数
function getRandom(min, max) {
return Math.random() * (max - min) + min;
}
console.log(getRandom(10, 20));
//得到一个两数之间的随机整数,包括两个数在内
function getRandomInt(min, max) {
min = Math.ceil(min);
max = Math.floor(max);
return Math.floor(Math.random() * (max - min + 1)) + min;
}
最初计算机操作系统是32位,而时间也是用32位表示。
System.out.println(Integer.MAX_VALUE);//2147483647
Integer 在JAVA内用32位表示,因此32位能表示的最大值是2147483647。另外1年365天的总秒数是 31536000,2147483647/31536000 = 68.1,也就是说32位能表示的最长时间是68年,从1970年开始的话,加上68.1,实际最终到2038年01月19日03时14分07秒,便会到 达最大时间,过了这个时间点,所有32位操作系统时间便会变为10000000 00000000 00000000 00000000,算下来也就是1901年12月13日20时45分52秒,这样便会出现时间回归的现象,很多软件便会运行异常了。
到这里,我想问题的答案已经显现出来了,那就是:因为用32位来表示时间的最大间隔是68年,而最早出现的UNIX操作系统考虑到计算机产生的年代和应用的 时限综合取了1970年1月1日作为UNIX TIME的纪元时间(开始时间),至于时间回归的现象相信随着64为操作系统的产生逐渐得到解决,因为用64位操作系统可以表示到 292,277,026,596年12月4日15时30分08秒,相信我们的N代子孙,哪怕地球毁灭那天都不用愁不够用了,因为这个时间已经是千亿年以后了。
Date对象 用来处理日期和时间。它和Math对象不一样,它是一个构造函数,需要实例化后才能使用。
//Date 获取当前时间
var now = new Date();
console.log(now); //Wed May 06 2020 18:03:17 GMT+0800 (中国标准时间)
//获取特定时间
var now = new Date('2019-5-6');
console.log(now); //Mon May 06 2019 00:00:00 GMT+0800 (中国标准时间)
可以通过获取时间的指定部分,拼接成自己想要的日期格式
案例:返回当前时间 格式 xx:xx:xx
function getTimer() {
var date = new Date();
var hour = date.getHours() < 10 ? '0' + date.getHours() : date.getHours();
var min = date.getMinutes() < 10 ? '0' + date.getMinutes() : date.getMinutes();
var sec = date.getSeconds() < 10 ? '0' + date.getSeconds() : date.getSeconds();
return hour + ':' + min + ':' + sec;
}
console.log(getTimer());
Date对象 是基于1970年1月1日(世界标准时间)开始算的,可以通过 valueOf() 和 getTime() 方法获取距今的总毫秒数,所得到的时间戳是独一无二的。
//通过 valueOf() getTime()
var date = new Date();
console.log(date.valueOf());
console.log(date.getTime());
//简单写法
var date1 = +new Date(); //+new Date() 返回总毫秒数
console.log(date1);
//H5 新增写法
console.log(Date.now());
案例:倒计时写法
因为得到的 date 不能直接进行相加减,所以需要用到时间戳。预设时间 减去 当前时间,得到相差毫秒数,将相差毫秒数除以1000得到相差秒数,然后分别进行除法求余运算得到各个单位的数值。
function countDown(time) {
var nowTime = +new Date(); //获取当前时间
var inputTime = +new Date(time); //获取预设时间
var times = (inputTime - nowTime) / 1000; //换算单位 ms -> s
var d = parseInt(times / 60 / 60 / 24); //得到天数
d = d < 10 ? '0' + d : d;
var h = parseInt(times / 60 / 60 % 24); //得到时
h = h < 10 ? '0' + h : h;
var m = parseInt(times / 60 % 60); //得到分
m = m < 10 ? '0' + m : m;
var s = parseInt(times % 60); //得到秒
s = s < 10 ? '0' + s : s;
return d + '天' + h + '时' + m + '分' + s + '秒';
}
console.log(countDown('2020-5-6 21:19:00')); //00天00时00分51秒
基本包装类型 就是把简单数据类型包装为复杂数据类型,这样基本数据类型就有了属性和方法。
为了方便操作基本数据类型,JS提供了三个特殊的引用类型:String、Number 和 Boolean。
字符串所有的方法都不会修改字符串本身(字符串是不可变的),操作完成会返回一个新的字符串。
var str = '改革春风吹满地,春天到啦';
console.log(str.indexOf('春')); //2
console.log(str.indexOf('春', 3)); //8
案例:查找字符串中某个字符出现的次数
var str = 'oabcoefoxyozzopp';
var count = 0;
var i = 0;
for (var j = 0; j < str.length; j++) {
if (str.indexOf('o', i) != -1) {
i = str.indexOf('o', i);
console.log(i);//0 4 7 10 13
i++;
count++;
}
}
console.log(count);//5
var str = 'andy';
console.log(str.charAt(0)); //a
console.log(str.charCodeAt(1));//110
console.log(str[2]);//d
案例:统计字符串中出现次数最多的字符及其出现次数
这是一道经典的算法题,2020年3月初面试字节跳动日常实习生时被问到。
var str = 'abjfkfsdfdmfpiptroqmx';
var obj = {};
var max = 0;
var ch = '';
//先统计各个字符出现的次数存于obj中
for (var i = 0; i < str.length; i++) {
var chars = str.charAt(i);
if (obj[chars]) {
obj[chars]++;
} else {
obj[chars] = 1;
}
}
//找出最大的出现次数及相应字符
for (var k in obj) {
if (obj[k] > max) {
max = obj[k];
ch = k;
}
}
console.log(max);//4
console.log(ch);//f
concat substr slice substring 都会返回一个字符串
var str = 'abc';
str = str.concat('efg');
console.log(str); //abcefg
str = str.substr(0, 3);
console.log(str); //abc
str = 'abcdefg';
str = str.slice(1, 3);
console.log(str); //bc
replace 替换字符
语法 replace('被替换的字符','替换为的字符') ,且它只会替换 第一个字符 , 该方法会返回一个字符串。
案例:替换一个字符串 'ahbjfhldiiiddsalomkkkal' 中所有的 'a' 为 '*' 。
var str = 'ahbjfhldiiiddsalomkkkal';
while (str.indexOf('a') != -1) {
str = str.replace('a', '*');
}
console.log(str);//*hbjfhldiiidds*lomkkk*l
split 将字符串分割为数组
语法 split(' 分隔符 ') ,它会返回一个数组
var str = 'a&b&c&d';
var arr = str.split('&');
console.log(arr); //["a", "b", "c", "d"]
通俗讲,其实 API 就是接口。
获取页面元素,进而可以操作元素。主要有以下几种方式:
1 因为文档页面从上往下加载,先要有标签,故script写在标签后面。
2 返回的是一个元素对象。
3 需要 console.dir 才能输出详细信息。
2020-5-8
1 返回带有指定标签名的对象的集合。
2 不需要 console.dir 也能输出详细信息。
- 知否知否,应是绿肥红瘦
- 知否知否,应是绿肥红瘦
- 知否知否,应是绿肥红瘦
- 知否知否,应是绿肥红瘦
- 知否知否,应是绿肥红瘦
这一些方法会存在兼容问题。
document.getElementsByClassName('类名') 根据类名返回元素对象集合
document.querySelector('选择器') 根据指定选择器返回第一个元素对象
document.querySelectorAll('选择器') 根据指定选择器返回所有符合的元素对象
- 知否知否,应是绿肥红瘦
- 知否知否,应是绿肥红瘦
- 知否知否,应是绿肥红瘦
- 知否知否,应是绿肥红瘦
- 知否知否,应是绿肥红瘦
当然,通过其他方法也可以获取。
var body = document.body;
console.log(body);
doucument.documentElement 获取html元素
当然,通过其他方法也可以获取。
var box = document.documentElement;
console.log(box);
获取子节点数组有两种方法,一种是用childNode属性,另一种是用children属性。
firstchild 和 lastchild 会返回一个子节点,但不一定是元素节点。
firstElementChild 会返回第一个元素子节点,lastElementChild 会返回最后一个一个元素子节点,但是它们会有兼容性问题。
方便且兼顾兼容性的 获取下一个元素兄弟节点 的方法
禁止鼠标右键菜单 contextmenu 、禁止鼠标选中 selectstart
mouseenter 和 mouseleave 事件
给元素添加事件,称为 注册事件 或者 绑定事件。
注册事件有两种方式:传统方式 和 方法监听注册方式
attachEvent 事件监听方式(了解)
这种方式是用于兼容ie8及更低版本的,并且该方法是ie专有的,不推荐使用。
给元素添加事件:1 获取事件源 2 绑定事件 3 添加事件处理程序
js 可以通过DOM操作改变网页内容、结构和样式等。
区别:innerText 不能识别html标签,非W3C标准,会去除空格和换行。
案例:
我是文字
效果:
通过修改这些元素属性可以动态修改页面,src、href 、id、alt 和 title。
通过修改这些表单元素属性可以动态修改页面,type、value 、checked、selected 和 disabled。
案例:
效果图:
JS 修改页面样式时,如果修改少量可以通过设置style的方式,否则可以通过设置class的方式。
案例:
获取属性有两种方法:
1 element.属性
2 element.getAttribute('属性');
区别:
element.属性 用于获取内置属性值,即元素本身自带的属性
element.getAttribute('属性'); 主要获得自定义的属性,即程序员自定义的属性
document.write 、element.innerHTML 、document.createElement
添加节点有两种形式:
第一种是 node.appendChild 添加到父节点的子节点列表末尾;
第二种是 node.insertBefore 添加到父节点的指定子元素的前面
复制节点可分为 深拷贝 和 浅拷贝
捕获阶段 & 冒泡阶段
e.target 指向的是触发的对象,比如点击对象;而,回调函数内this指向的是绑定事件的对象,两者不一定相同。
阻止事件冒泡用 e.stopPropagation()
坐标属性
一般我们是不能直接访问 global 对象的。故在浏览器上,它提供的 window 对象扮演了 global 的角色。
一种是window的 load 事件,另一种是window的 DOMContentLoaded 事件。
var time1 =setTimeout(fn1,3000);
var time2 =setTimeout(fn2,5000);
1、全局作用域或者普通函数中 this 指向全局对象window (注意定时器里面的this指向window)
2、方法调用中谁调用 this 指向谁
3、构造函数中 this 指向构造函数的实例
JS 语言的一大特点就是单线程。
同步和异步的本质区别:这条流水线上各个流程执行顺序不同。
Document
//页面a.html
// 页面b.html
在 js 里面获取元素的宽高等属性,只能通过 element.style.width 和 element.offsetWidth ,而不能用 element.width。前者只能获取行内样式的属性,后者可以获取内嵌样式等表的属性。
e.pageX 和 e.pageY 是鼠标相对于DOM窗口的坐标,而 offsetTop 和 offsetLeft 是元素距离页面窗口的距离。两者相减,就能得到鼠标在元素内的坐标。
效果:
代码:
Document
可以加函数名
(function fn() {})()
(function fn() {}())
元素被卷去的头部 element.scrollTop , 页面被卷去的头部 window.pageYOffset。
DTD 指的是
通过定时器 setInterval 不断使元素发生变化
Document
使用 window.scroll( x , y ) 即可。
有 三种 解决方案
https://www.swiper.com.cn/
superslide 、iscroll 、 zy.media.js
特点:单页面内使用(共享),关掉页面就没了
Document
特点: 同一浏览器内通源页面共享,不删除则永久存在
与 sessionStorage 类似
Document
与 sessionStorage 类似
注意 jQuery对象只能使用jQuery对象属性方法;DOM对象只能使用原生的JS属性和方法
补充: DOM对象 转换为 jQuery对象还有一种方式:
var myDiv = document.querySelector('div');
$(myDiv).hide();
Document
方便书写链式编程。
如果需要返回所有父级元素,可用 .parents() 或 .parents(selector) 。
取返回的伪数组中的某个元素,用 eq(index) 得到 jq对象;若用 [index] 得到 dom对象。
// 2
$('.wrapper').css('height', 240); //√
$('.wrapper').css('height', '240px'); //√
$('.wrapper').css('height', 240 px); //×
// 3
$('.wrapper').css({
'color': 'red',
'font-size': '20px',
'height': 280
}); //√
$('.wrapper').css({
color: 'red',
fontSize: '20px',
height: 280
}); //√
animate 是元素动画,不能给document对象设置动画。
$('button').eq(3).click(function () {
$('div').animate({
width: 500,
height: 400
}, 100);
});
读取自定义属性值时,不需要加"data-"前缀。
文本框的值用 val()
把 Number 四舍五入为指定小数位数的数字
//规定小数的位数,是 0 ~ 20 之间的值,包括 0 和 20,有些实现可以支持更大的数值范围。如果省略了该参数,将用 0 代替。
NumberObject.toFixed(num)
注意 : prepend 可以追加在内部的 前面 。
只能获取,不能设置
优势3的本质是事件委托,把事件绑定到动态生成的元素的父元素上。如此一来无论新建多少元素,这些元素都能触发相应事件。
绑定方式与 on 相似。
只是不需要特定的触发方式便能调用,如果要实现“自动”,仍然需要定时器。
语法解释:把 object 复制(合并)到 target 里去。
如果是浅拷贝,对于复杂数据类型,相当于是复制了引用,并且会复制完成后会覆盖冲突的数据。
如果是深拷贝,对于复杂数据类型,相当于是复制了相同数据,而复制完成后对于冲突数据,会合并而非覆盖。
方法1:使用 jQuery 代替 $
方法2:释放 jQuery 对 $ 的控制权,让用户自定义
index() 方法返回指定元素相对于其他指定元素的 index 位置,如果未找到元素,index() 将返回 -1。
$("li").click(function(){
alert($(this).index());
});
类名一般大写,且类名后面不加小括号。
class Star {
constructor(name, age) {
this.name = name;
this.age = age;
}
}
var ldh = new Star('liudehua', 18);
var zxy = new Star('zhangxueyou', 20);
多个方法之间不用写逗号。
1 ES6的类中没有变量提升,必须先要定义类,才能实例化类
2 类里面的共有属性和方法一定要加 this 才能使用
3 constructor 里面的 this 指向实例对象,方法里面的 this 一般指向这个方法的调用者
就近原则:实例化子类调用方法时,会先查找子类中有无该方法,如果没有再去父类中查找。
super (parameter) 调用父类构造函数
super.xxx (parameter) 调用父类普通函数
注意 构造函数内使用super,必须把super放到最前面。
class Father {
constructor(x, y) {
this.x = x;
this.y = y;
}
sum() {
console.log(this.x + this.y);
}
}
class Son extends Father {
constructor(x, y) {
super(x, y)
}
}
var son = new Son(1, 2);//3,因为子类对象里面没有x,y只能去父类对象中取
son.sum();
class Father {
constructor(x, y) {
this.x = x;
this.y = y;
}
sum() {
console.log(this.x + this.y);
}
}
class Son extends Father {
constructor(x, y) {
super(x, y);
this.x = 2;
this.y = 5;
}
}
var son = new Son(1, 2);//7
son.sum();
//情形 1 输出 hello!
class Father {
say() {
console.log('hello!');
}
}
class Son extends Father {
}
var son = new Son();
son.say();
//情形2 输出 hi~
class Father {
say() {
console.log('hello!');
}
}
class Son extends Father {
say() {
console.log('hi~');
}
}
var son = new Son();
son.say();
//情形3 输出 hello! hi~
class Father {
say() {
console.log('hello!');
}
}
class Son extends Father {
say() {
super.say();
console.log('hi~');
}
}
var son = new Son();
son.say();
ES5是通过原型来模拟继承的操作的,然后ES6大概就是将ES5的这些操作封装起来。
ES6 子类 extends 父类 时,如果子类有 constructor(构造函数) ,则一定要在子类 constructor 内使用 super 调用父类的 constructor,否则会报错。
成功创建子类对象实例之后,调用某一个方法的查找顺序是这样的,先看子类对象中有无该方法,若有则执行,若无则再查找父类,以此类推。属性的查找同理。
//实例成员就是构造函数内部通过this添加的成员,只能通过实例化的对象来访问
function Star(name) {
this.name = name;
}
var star = new Star('lili');
console.log(star.name); //lili
//静态成员就是在构造函数本身添加的成员,只能通过构造函数来访问。
Star.sex = 'man';
console.log(Star.sex); //man
浪费内存。因为对于相同的方法,实例之间是各自拥有,而不是共享。
function Star(name) {
this.name = name;
}
Star.prototype.sing = function () {
console.log('我会唱歌');
}
var star1 = new Star('lili');
var star2 = new Star('dudu');
console.log(star1.sing === star2.sing); //true
如果覆盖了构造函数的原型对象,需要让原型对象内的 constuctor 重新指向原来的构造函数。
function Star(name) {
this.name = name;
}
Star.prototype = {
constructor: Star,
sing: function () {
console.log('sing');
},
movie: function () {
console.log('moveie');
}
}
var star1 = new Star('lili');
var star2 = new Star('dudu');
1 构造函数里面的 this 指向的是实例化所得到的实例对象
2 原型对象 prototype 函数里面的 this 指向的是调用者,即实例对象
ES5并没有提供ES6的 extends 继承,所以我们通过 构造函数 + 原型对象 来模拟实现继承,称为组合继承。
如果没有参数,则直接像普通函数一样运行。
var name = 'lolo';
var star = {
name: 'Jane'
}
function fn() {
var name = 'sony';
console.log(this.name);
}
fn(); //lolo
fn.call(star); //Jane
公有方法写在父类构造函数中即可
function People(name, age) {
this.name = name;
this.age = age;
}
function Newpeople(name, age, sex) {
//在父类属性基础上,增加一个sex
this.sex = sex;
People.call(this, name, age);
}
var newpeople = new Newpeople('sony', 18, 'man');
console.log(newpeople); //{age: 18,name: "sony",sex: "man"}
不直接通过修改 Son.prototype.__proto__属性 进行继承,可能是因为__proto__属性是非标准属性,不能随意赋值。
function Father() {
}
//父类原型对象中的共有方法
Father.prototype.money = function () {
console.log(10000);
}
function Son() {
}
Son.prototype = new Father();
Son.prototype.constructor = Son;
var son = new Son();
son.money();
函数内遇到 return true 不会终止迭代。
var arr = ['a', 'b', 'c', 'd'];
arr.forEach(function (value, index, arr) {
console.log(value, index); //a 0 、 b 1 、 c 2 、 d 4
})
函数内遇到 return true 不会终止迭代。主要用于过滤数据,当然也可以遍历。
var arr = [12, 34, 56, 78, 90];
var newArr = arr.filter(function (value, index, arr) {
return value >= 40;
})
console.log(newArr); //56,78,90
函数内遇到 return true 会终止迭代,也可用于遍历。
var arr = [12, 34, 56, 78, 90];
var flag = arr.some(function (value, index, arr) {
return value >= 40;
})
console.log(flag); //true
var obj = {
name: 'liya',
age: 18,
sex: 'man'
};
var arr = Object.keys(obj);
console.log(arr); //["name", "age", "sex"]
var name = 'sandy';
var o = {
name: 'andy'
};
function fn() {
console.log(this.name);
}
fn.call(o); //andy
fn(); //sandy
传参方式特别,apply 常搭配 Math 方法使用。
//因为Math对象的一些方法只能接收一个个的参数,而不是接收一整个的数组。
//而apply方法接收一个参数数组,并可以将数组元素作为参数一个个传给调用的方法。
//apply与Math配合起来,就能将一个个数字传给Math方法,从而可以达到数组也能使用Math方法的效果
function fn2(num1, num2, num3) {
console.log(num1 + num2 + num3);
}
fn2.apply(window, [2, 6, 3]);
不调用函数,但会改变函数 this 的指向。
var o = {
name: 'soso'
}
function fn(a, b) {
console.log(this);
console.log(a + b);
}
var f = fn.bind(o, 1, 3);
f(); //{name: "soso"} & 4
1 延伸了变量的作用范围。
2 利用闭包可以解决一些同步异步产生的问题,详情看案例。
//闭包指向fn
function fn() {
var num = 10;
return function () {
console.log(num);
}
}
var f = fn();
f();
Document
- 第一个
- 第二个
- 第三个
- 第四个
Document
- 第一个
- 第二个
- 第三个
- 第四个
//本题不存在闭包
var name = 'the windows';
var object = {
name: 'my object',
getNameFunc: function () {
var name = 'inner';
return function () {
return this.name;
};
}
};
console.log(object.getNameFunc()()); //the windows
//本题存在闭包
var name = 'the windows';
var object = {
name: 'my object',
getNameFunc: function () {
var that = this;
return function () {
return that.name;
};
}
};
console.log(object.getNameFunc()()); //my object
var n = 1;
function fn() {
console.log('第' + n + '次打印');
if (n === 6) {
return false;
}
n++;
fn();
}
fn();
//利用递归函数求 n 的阶乘
function fn(n) {
if (n == 1) {
return 1;
}
return n * fn(n - 1);
}
console.log(fn(3));
//利用递归函数求斐波那契数列第 n 项
// 1 1 2 3 5 8 13 21....
//我们只需要知道用户输入的n的前两项(n-1) 和 (n-2) 即可算出第n项
function fn(n) {
if (n === 1 || n === 2) {
return 1;
}
return fn(n - 1) + fn(n - 2);
}
console.log(fn(8));//21
//data如下,从大类到细类,各个类都有对应的id
//查找某id对应的类别
var data = [{
id: 1,
name: '家电',
goods: [{
id: 11,
gname: '冰箱'
}, {
id: 12,
gname: '洗衣机'
}]
}, {
id: 2,
name: '服饰'
}];
function getId(data, id) {
data.forEach(function (item) {
if (item.id == id) {
console.log(item);
} else if (item.goods && item.goods.length > 0) {
getId(item.goods, id)
}
})
}
getId(data, 12); //{id: 12, gname: "洗衣机"}
利用 for in ( for in 得到的是属性名)。但是对于复杂数据,比如 own,复制的是地址,所以 o 修改了 own ,obj 中的 own 也会变化。
var obj = {
id: 1,
name: 'lily',
own: {
money: 100
}
};
var o = {};
for (var k in obj) {
//k是属性名
o[k] = obj[k];
}
不需要再用 for in。
var obj = {
id: 1,
name: 'lily',
own: {
money: 100
}
};
var o = {};
Object.assign(o, obj);
用 for in 递归遍历,注意 array 是复杂数据类型并且也是对象类型 。
var obj = {
id: 1,
name: 'lily',
own: {
money: 100
}
};
var o = {};
function deepCopy(newobj, oldobj) {
for (var k in oldobj) {
//需要判断属性属于哪种数据类型
//先把属性赋值给item
var item = oldobj[k];
//如果item是数组类型(数组也是复杂数据类型),需要遍历复制
if (item instanceof Array) {
newobj[k] = [];
deepCopy(newobj[k], item);
//如果item是数组外的对象类型,需要遍历复制
} else if (item instanceof Object) {
newobj[k] = {};
deepCopy(newobj[k], item);
//其他基本类型直接赋值
} else {
newobj[k] = item;
}
}
}
deepCopy(o, obj);
另外,还可以借用 JSON.parse 和 JSON.stringify 来实现深拷贝。
var reg2 = /^123$/;
console.log(reg2.test(123)); //true
console.log(reg2.test(123123)); //false
// [] 表示有一系列字符可供选择,只要匹配其中一个即可
var reg = /[abc]/; //内容只要包含abc其中一个即可
console.log(reg.test('andy')); //true
console.log(reg.test('red')); //false
var reg = /^[abc]$/; //三选一,内容只能是a 或 b 或 c
var reg = /^[a-z]$/; //内容只能是26个字母中的一个
var reg = /^[a-zA-Z0-9-_]$/; //内容只能是其中一个
var reg = /^[^a-zA-Z0-9-_]$/; //一个内容,但不能包含其中的任何一个字符/数字
设定前面模式的出现次数
var reg1 = /^a*$/;
console.log(reg1.test('aaaa')); //true
var reg2 = /^a+$/;
console.log(reg2.test('aaaa')); //true
var reg3 = /^a?$/;
console.log(reg3.test('aaaa')); //false
var reg4 = /^a{2}$/;
console.log(reg4.test('aaaa')); //false
var reg5 = /^a{2,}$/;
console.log(reg5.test('aaaa')); //true
var reg6 = /^ab{2,4}$/;
console.log(reg6.test('abbb')); //true
var reg7 = /^(ab){2,4}$/;
console.log(reg7.test('abababab')); //true
var reg8 = /^[a-zA-Z0-9-_]{2,8}$/;
console.log(reg8.test('abb-_bb')); //true
//把所有的开心或快乐替换成兴奋
var newString = oldString.replace(/开心|快乐/g, '兴奋');
if (true) {
let a = 10;
var b = 20;
}
console.log(b); //20
console.log(a); //报错
1 let 不存在变量提升,需要先声明,才能使用。
2 let 会产生暂时性死区。
//let 会产生暂时性死区。
//在块级作用域内部,只要有let声明某个变量,这个变量就不同于外面的变量,是属于块级作用域的,与块级作用域绑定了。
//所以在这个if里面,有一个块级作用域的num。
//然后这里先使用num再声明num,而let没有变量提升,故报错。
var num = 10;
if (true) {
console.log(num);//报错
let num = 20;
}
这里因为是使用 var 来声明 i,所以 for 产生的两个作用域都属于全局作用域。因此当函数要打印 i 的时候,所用到的是全局作用域的 i,且 i=2。
这里因为是使用 let 来声明 i,所以for产生的两个作用域都属于块级作用域。第一个块级作用域里面的 i=0,第二个 i=1。当函数打印 i 时,会去自己对应的上级作用域寻找 i,所以就输出了相应的块级作用域里面的 i 。
对于复杂数据类型,可以修改其属性或者内部的某个值,但是依然不可更改其地址。
一般定义不会更改的函数以及常数可以使用 const ,这样JS解析不需要实时监控变化,效率更高。
用 const 的时候,对于复杂数据类型,可以修改其属性或者内部的某个值,但是依然不可更改其地址。
方便从数组中取值。平时我们从数组取值,可能需要多个声明,一个个地进行赋值,现在一句话就可以搞定。
var arr = [1, 2, 3, 4];
//这里相当于 a b c d e 都是用var声明的
var [a, b, c, d, e] = arr;
console.log(a); //1
console.log(b); //2
console.log(c); //3
console.log(d); //4
console.log(e); //undefined
解构时的变量名,一定要与对象中的属性名相匹配,才可以解构成功。
var obj = {
name: 'lily',
age: 18,
sex: 'man'
};
//这里相当于花括号内的属性都是用var声明的
var {name, age, sex, money} = obj;
console.log(name); //lily
console.log(age); //18
console.log(sex); //man
console.log(money); //undefined
//如果用 xx:xx 的形式,则左边的是所匹配的属性,右边的是变量名(属性别名)
var {name:myname, age:myage, sex:mysex, money:mymoney} = obj;
console.log(myname); //lily
console.log(myage); //18
console.log(mysex); //man
console.log(mymoney); //undefined
注:
1 => 右边只有一句代码即可省略 {} ,而非一定需是返回值。(maybe)
2 箭头函数内是用不了 arguments 的,只能用 剩余参数 的方法获取全部形参。
//上面图片的代码输出的是两个obj对象
//下面这段代码输出的是两个window对象
//因为箭头函数本身不绑定this,所以它所在的区域this指向的是谁,它里面的this就指向谁
//图片案例里面,this指向obj;下面代码中,this指向window
function fn() {
console.log(this);
return () => {
console.log(this);
}
}
const obj = {
name: 'zhangsan'
};
const resFn = fn();
resFn();
//因为箭头函数没有自己的this,所以要看它所处的区域的this指向谁。
//它所处的区域为obj,obj是对象,不能自己构建一个作用域,obj本身也属于全局作用域
//所以say方法中的this指向window
var age = 100;
var obj = {
age: 20,
say: () => {
alert(this.age);
}
}
obj.say(); //100
const sum = (...args) => {
let total = 0;
args.forEach(item => total += item);
return total;
}
console.log(sum(10, 20, 50));//80
let students = ['张三', '王五', '李四'];
let [s1, ...s2] = students;
console.log(s1); //'张三'
console.log(s2); //['王五','李四']
let arr = [1, 2, 3, 4];
console.log(...arr); // 1 2 3 4 ,等价于下面的写法。
console.log(1, 2, 3, 4);
var arr1 = [1, 2, 3];
var arr2 = [4, 5, 6];
console.log([...arr1, ...arr2]);
arr1.push(...arr2);
console.log(arr1);
a
b
除了用扩展运算符的方法(上面代码),还可以使用 Array 构造函数 中的 from 方法。
a
b
let arrayLike = {
'0': 1,
'1': 2,
'length': 2
}
let newArr = Array.from(arrayLike, item => item * 2);
console.log(newArr);
有了模板字符串(反引号字符串),在拼接字符串的时候就不需要再用 + + 了。
//变量没有要求必须为反引号字符串,当然反引号字符串也可以
var str = 'lily';
//反引号字符串才能解析变量,普通字符串内部不会解析${}
var sayHello = `hello,my name is ${str}`;
console.log(sayHello); //hello,my name is lily
const s = new Set(['a', 'a', 'b', 'b']);
console.log(s.size); //2
const arr = [...s];
console.log(arr); //(2) ["a", "b"]
object.keys(obj).length === 0