一、JavaScript基础
1、五种基本数据类型
- 字符类型‘11’被引号引起来的数据 ---- string
- 数值型 123 正数、负数、小数、NaN ---- number
- 布尔型 只有true和false ---- boolean
- 空值型 只有null ---- object
- 未定义型 只有undefined ---- undefined
typeof 检测基本数据类型 console.log(typeof data1);
2、数据类型的转换
- 强制转换:string(XXX); XXX.to string();
- 隐式转换:+string 可以转成数字, a+""可以转成字符串,!var 可以转成布尔值
转换成为false的6种情况:null underfined false 0 NaN ‘’
3、自调用函数
var result = (function (age = 18) {
alert('这个人的年龄是' + age);
return age;
})(20);
console.log(result);
4、Math对象(不需要new关键词创建)
Math.round() 四舍五入
Math.ceil() 向上取整
Math.floor() 向下取整
Math.random() 获取随机数
Math.floor(Math.random()*(5+1))
function randomint(num = 5) {
return Math.floor(Math.random() * (num + 1)); //返回随机数
}
5、几种遍历方法
- for…in
for (const key in obj){
console.log('属性名',key,'值',obk[key]);
}
- Object.keys(obj) Object.values(obj)
const obj = {
id:1,
name:'小明',
age:18
}
console.log(Object.keys(obj))
console.log(Object.values(obj))
- forEach()遍历普通数组
const arr = [
{ id: 1, name: '小明' },
{ id: 2, name: '小红' },
{ id: 3, name: '小吕' }
]
// 写法1
arr.forEach(function (item) {
console.log(item.id + '---' + item.name)
})
// 写法2
arr.forEach( (v)=> {
console.log(v.id + '---' + v.name)
})
- map()映射数组
const arr = [
{ id: 1, name: '小明' },
{ id: 2, name: '小红' },
{ id: 3, name: '小吕' }
]
// 写法1
arr.map(function (item) {
console.log(item.id + '---' + item.name)
})
// 写法2
arr.map( (v)=> {
console.log(v.id + '---' + v.name)
})
- forEach:用来遍历数组中的每一项,没有返回值,不影响原数组
- map:相当与原数组克隆了一份,支持return,可以改变克隆的每项,也不影响原数组
6、数组对象的方法
push 向数组的末尾添加一个或更多元素,并返回新的长度
用法:arrs.push('d', 'e', 'f')
unshift 向数组的开头添加一个或更多元素,并返回新的长度
用法: arrs.unshift(44, 55, 66);
pop 删除并返回数组的最后一个元素
用法:arrs.pop();
shift 删除并返回数组的第一个元素
用法:arrs.shift();
reverse 颠倒数组中元素的顺序
用法:.reverse();
sort 对数组的元素进行排序
从小到大 用法:arrs.sort(function (a, b) {
return a - b;
});
splice 删除元素,并向数组添加新元素
用法:
// 参数1:要删除元素的索引值 可以用-1代表最后一位
// 参数2:删除的个数
// 参数3往后:添加的元素
arrs.splice(0, 1); //删除第一个
arrs.splice(arrs.length, 0, 55, 66, 77); // 在最后追加数据
slice 从某个已有的数组返回选定的元素
用法:
// 参数1:选取的开始索引值
// 参数2:选取的结束索引值(不包括该元素)
arrs.slice(1, 3); //抽取第2第3个数
join 把数组用分隔符号连接成新的字符串
用法:
arrs.join(',')//用逗号分隔
.concat() 连接两个或更多的数组,并返回结果。
用法:
// 把 boys 和 girls 合并成一个新的数组all
写法1: const all = [].concat(boys, grils);
写法2: const all = boys.concat(grils);
indexOf 返回当前查找数据的索引值
// 参数1: 要查找的位置的数据
// 参数2: 查找起始索引值
用法:arrs.indexOf(33, 3); //从第4位开始查找33
includes() 判断一个数组是否包含一个指定的值
用法: array1.includes(3); //查看是否包含3
.forEach() 遍历数组
用法:
写法1:arrs.forEach(function (item, index) {
console.log(item, index);
});
写法2:arrs.forEach((item, index) => console.log(index, item));
.map() 创建一个新数组,给元素加标签
用法:
写法1: const newArrs = arrs.map(function (item) {
return '<a>' + item + '</a>';
});
写法2:const newArrs = arrs.map(item => '<a>' + item + '</a>');
.filter() 过滤原数组返回一个新数组
用法: const result = arrs.filter(function (item) {
// return 返回满足过滤规则的数据
return item > 30;
});
7、string 对象方法
.split() 把字符串分割为字符串数组
用法:
const strObj = 'apple-banner-cat';
const arrs = strObj.split('-');//根据-来分隔
.substring() 提取字符串中两个指定的索引号之间的字符
用法: var str = "Hello world!";
console.log(str.substring(3, 7));//提取4~6的字符
.replace() 替换字符
用法:const strObj = new String('吃饭了吗?');
const newStr = strObj.replace('吗?', '。');
.toLowerCase() 把字符串转换为小写
.toUpperCase()`把字符串转换为大写
用法: const lowStr = str.toLowerCase();
const lowStr = str.toUpperCase();
.trim() 消除字符串空格
用法:
const str = ' 你 好 吗? ';
const newStr = str.trim();
.charAt() 返回在指定位置的字符
用法:
var str="你好呀,在吃饭了吗"
console.log(str.charAt(5))
8、事件冒泡和事件委托
- 事件冒泡:当一个元素上的事件被触发的时候,比如说鼠标点击了一个按钮,同样的事件将会在那个元素的所有祖先元素中被触发.这一过程被称为事件冒泡;这个事件从原始元素开始一直冒泡到 DOM 树的最上层。
- 事件委托:让利用事件冒泡的原理,让自己的所触发的事件,让他的父元素代替执行。
- 阻止默认事件:为了不让 a 点击之后跳转,我们就要给他的点击事件进行阻止 return false; e.preventDefault();
div.onclick = function (e) {
// 标签名判断的时候注意大小写
if (e.target.nodeName === 'LI') {
alert('实现方式2:你点击了li');
}
}
father.onclick = function (e) {
console.log('是否包含 on 类名', e.target.classList.contains('on'));
if (e.target.classList.contains('on')) {
alert('你点击带 on 类名的 元素');
}
}
事件委派添加li 的 父级只需要给父级绑定一次即可只需要确保 li 的父级在页面中即可。事件委派在 列表数据量大的时候,更有优势,如 微博首页数据都是数百上千条,微博列表内容都是后添加的内容。
二、JavaScript高级
1、call , apply , bind 三个方法
- call , apply , bind 三个方法 可以改变函数内部 this 指向
- call, apply 和 bind 区别:call ,apply 会直接调用函数,而 bind 返回一个新的函数
- apply 和 call 的区别:apply 只能传两个实参,第二个实参是数组格式,call 每个实参单独书写
- bind 和 call 区别:bind 只是把第一个实参传递过去,并在堆空间复制一份新的函数,以返回值形式接受
hhh.apply([11, 22], ['你好', '你不好']);
hhh.call([11, 22], '你好', '你不好');
hhh.bind([11, 22], '你好', '你不好')
2、构造函数
// 构造函数 ------ 标准写法首字母需要大写
const Person = function () {
}
// 原型 ------- 函数对象自带原型,在原型上添加的方法会表现在<实例>的 __proto__ 上
Person.prototype.sayHi = function () {
console.log('新的对象能说话', this); // 原型上添加的方法,谁调用了方法,this 就指向谁。
}
// 实例 ------- 把实例对象的内存地址赋值给 p1
const p1 = new Person();
p1.sayHi();
封装一个jquery库(完整版可以看我的上传资源)
/*!
* my jQuery JavaScript Library v0.0.1
*
* Date: 2019-11-07
*/
(function () {
// 创建 $ jQuery 函数
const $ = function (selector) {
// 内部封装变得复杂了,但是对于用户使用来说变得简单了,通过 $() 就可以创建实例
return new Init(selector);
}
// 通过给 window 对象添加 $ 属性,让内部的 $ 地址添加到 window 对象上
window.$ = window.jQuery = $;
// 构造函数
const Init = function (selector) {
// 先给实例对象添加 selector 选择器属性,记录起来而已
this.selector = selector;
// 通过选择器查找到所有 dom 元素
const doms = document.querySelectorAll(selector);
// 先给实例对象添加 length 长度属性
this.length = doms.length;
// 先把外面的 this 的内存地址先存起来
const that = this;
// 遍历所有的 dom
doms.forEach(function (item, index) {
that[index] = item;
});
}
// 原型上添加方法
// addClass 添加类
Init.prototype.addClass = function (className) {
// 传递数据:
// 形参:className 接收传递的实参
// this:调用的对象会自动传过来,把内存地址给 this
// 遍历过程 - 隐式迭代核心
Array.from(this).forEach(function (item) {
item.classList.add(className);
});
// return this - 链式编程核心
return this;
}
// removeClass 删除类
Init.prototype.removeClass = function (className) {
// 遍历过程 - 隐式迭代核心
Array.from(this).forEach(function (item) {
item.classList.remove(className);
});
// return this - 链式编程核心
return this;
}
// toggleClass 切换类
Init.prototype.toggleClass = function (className) {
// 遍历过程 - 隐式迭代核心
Array.from(this).forEach(function (item) {
item.classList.toggle(className);
})
// return this - 链式编程核心
return this;
}
// 高阶函数 - 1. 把函数作为参数进行传递
// each 遍历函数
Init.prototype.each = function (fn) {
// console.log(fn); // 接收到传递过来的函数地址,保存到 fn 形参中
// fn(); // 直接调用 fn(),地址上的 index 和 item 就没有值,所以调用的时候要传参
for (let i = 0; i < this.length; i++) {
// fn 函数是传过来的,在 each 内部调用,传递参数 i 作为 index,传递 this[i] 作为 item
// 通过 call 或 apply ,可以在调用函数的时候,把第一个实参传递过去给内部this
// 预期希望,调用的时候 item === this
fn.call(this[i], i, this[i]);
// fn(i, this[i]);
}
// return this - 链式编程核心
return this;
}
// show() 显示
Init.prototype.show = function () {
// console.log('外面函数的this代表调用的JQ实例',this);
// 遍历过程 - 隐式迭代核心
this.each(function () {
// console.log('内部函数的this变成了每一个元素 item',this)
this.style.display = 'block';
});
// return this - 链式编程核心
return this;
}
// hide() 隐藏
Init.prototype.hide = function () {
// 遍历过程 - 隐式迭代核心
return this.each(function () {
this.style.display = 'none';
});
// return this - 链式编程核心
// return this;
}
// toggle() 切换
Init.prototype.toggle = function () {
return this.each(function () {
// console.log(this)
// debugger;
// getComputedStyle() 获取计算样式,是否为 none
if (window.getComputedStyle(this).display === 'none') {
// 设置成变成默认值,因为 block 会直接改变显示模式,变成块级元素显示
this.style.display = 'block';
} else {
this.style.display = 'none';
}
});
}
})()
3、闭包:闭包其实就是一个局部变量作用域的延伸
闭包作用:
- 没有形成闭包的量,会自动被垃圾回收清理掉。
- 形成闭包的量就会被保留下来,当里函数调用的时候,会自动找到父级中的定义的量。
- 形成闭包的局部变量可以被后续修改。
书写时形成闭包的情况
- 函数嵌套函数。
- 外函数定义了局部量。
- 里函数使用了外函数的局部量。
- 里函数能被调用了,里函数的代码运行。
var name = "The Window";
var object = {
name: "My Object",
getNameFunc: function () {
var that = this; // 父级作用域下的 this,就是对象自己 object
return function () {
return that.name; // that 是代表父级作用域下的 this,所以里面的代码相当于访问了 object.name
};
}
};
var res = object.getNameFunc();
var LifnRes = res();
console.log(LifnRes); // "My Object"
- 优点:使用闭包可以形成独立的空间,缓存数据,延长变量的生命周期
- 缺点:延长了作用域链,需要释放的变量不能及时释放,可能引发内存泄漏
4、递归:程序在执行过程中不断调用自身
- 递进:函数自己调用自己。
- 回归:内部通过 return 回归,最终的回归条件记得写前面。
阶乘
const fn = function (n) {
if (n === 1) {
return 1;
}
const result = n * fn(n - 1);
return result;
}
const result = fn(4);
console.log(result);
5、继承:原型继承,组合继承,类继承
- 原型继承
// Person 构造函数
const Person = function (name = '小明', age = 18, gender = '男') {
this.name = name;
this.age = age;
this.gender = gender;
}
// run 的方法添加给了 Person 的构造函数
Person.prototype.run = function () {
console.log(this, '人能跑');
}
// Student 构造函数 Student 继承与 Person
const Student = function (name = '学生老明', age = 58, gender = '男', number = 666) {
Person.call(this, name, age, gender);
this.number = number;
}
// new Person() 创建一个人对象
// 赋值给 Student.prototype 把创建的人实例,作为学生实例的父级
Student.prototype = new Person();
// 由于是赋值覆盖,所以 constructor 没有了,手动添加一下 constructor 属性
Student.prototype.constructor = Student;
Student.prototype.coding = function () {
console.log(this, '学生能写代码');
}
const s1 = new Student();
// console.log(s1);
s1.coding();
s1.run();
console.log(s1.constructor);
- 类继承
// 类语法 class
class Person {
// constructor 构造函数
constructor(name = '人的名字', age = 0) {
this.name = name;
this.age = age;
}
// 方法自动添加到原型上,相当于 Person.prototype.run = function(){ }
run() {
console.log('人能跑');
}
}
// 通过 extends 实现 类继承
class Student extends Person {
// constructor 构造函数
constructor(name = '学生名字', age = 6, number = 666) {
// 通过 extends 继承的类,constructor 构造函数内部必须调用一下 super()
// super() 自动把 this 传递,自动把 name 和 age 传递,相当于 Person.call(this,name,age);
super(name, age);
this.number = number;
}
// 方法自动添加到原型上,相当于 Student.prototype.coding = function(){ }
coding() {
console.log('学生能写代码');
}
}