提示:自己记笔记用的
JS进阶
- 面向对象
- class类
- 箭头函数
- this
- 递归
- 异常捕获
- 严格模式
- 严格模式不允许使用未定义的变量
- 严格模式静止对只读属性赋值
- 不能删除 不可删除属性
- 禁止函数参数名重名
- 禁止8进制数
- 禁止设置原始类型(primitive)值的属性*(**function**() {*
- 禁止with
- 禁止使用eval()创建的变量
- 禁止删除变量
- 禁止使用arguments和eval作为变量名
- 严格模式下,函数的 arguments 对象会保存函数被调用时的原始参数。arguments[i] 的值不会随与之相应的参数的值的改变而变化,同名参数的值也不会随与之相应的 arguments[i] 的值的改变而变化。
- 不再支持 *arguments.callee*
- 禁止this指向全局对象,当this指向全局对象时,自动转为undefined
- Object.defineProperty设置高级属性
- 闭包
- 深拷贝与浅拷贝
- 正则
- 设计模式
- hasOwnProperty()
- in关键字
- instanceof
- isPrototypeOf
- filter
- find
- Object.assin
- with()
- eval()
- Object.create()
- 解构
面向对象
三大特征:
封装性:将代码方法封装到对象中
继承性:子类能继承父类的方法
多态性:表现多种形态的能力
对象
对象是键值对的集合,具备某些能力的事物
对象中不同属性间用,隔开,对象中不能创建变量
// 函数创建
let a = new Object();
a.name = "亚索"
a.tips = "狂风绝息斩"
// 字面量创建
let b = {
name = "永恩",
tips="封尘绝念斩"
}
操作对象属性:
a.name = 值
a["name"] = 值
使用构造函数创建对象:建议函数名首字母大写
function Fun(name,age){
this.name = name;
this.age = age;
this.fn= function(){
console.log(1)
}
}
// 使用new关键字 实例化对象
let a = new Fun("一生独一",23);
a.fn() // 1
构造函数中将函数放在里面,每次调用都会声明一次,会造成内存浪费
原型
每个函数在声明时都会被分配一个原型对象,可以用来给实例化对象共享方法
function Fun(name,age){
this.name = name;
this.age = age;
}
// 构造函数.prototype.方法名 = 函数
Fun.prototype.fn = function(){}
let a = new Fun("啊卡丽",28)
a.fn()
原型图
原型链
Object是所有对象的先辈
要更改一个实例对象的父辈指向
所有函数的__proto__都指向Function原型,所有函数都是Function函数new出来的
封装一个简单的JQ
// 封装JQ对象
function JQuery(obj) {
let dom = document.querySelectorAll(obj);
for (let i = 0; i < dom.length; i++) {
this[i] = dom[i];
}
this.length = dom.length;
return this;
}
// 简化
function $(obj) {
return new JQuery(obj);
}
let dv = $("div");
// 封装JQ。css()方法
JQuery.prototype.css = function (type, value) {
// 判断传进来的是对象
if (arguments.length == 1) {
// 将对象属性名遍历
for (let key in type) {
// 给每个JQ对象添加
for (let i = 0; i < this.length; i++) {
this[i].style[key] = type[key];
}
}
} else {
for (let i = 0; i < this.length; i++) {
this[i].style[key] = value;
}
}
// 返回JQ对象 用于链式编程
return this;
};
// 封装JQ事件
JQuery.prototype.on = function (type, fun) {
// 给每个JQ对象添加
for (let i = 0; i < this.length; i++) {
// 判断游览器是否兼容
if (typeof this[i].addEventListener == "function") {
this[i].addEventListener(type, fun);
} else {
// ie 的注册事件
this[i].attachEvent("on" + type, fun);
}
}
return this;
};
// 封装each方法
JQuery.prototype.each = function (fn) {
for (let i = 0; i < this.length; i++) {
fn(i, this[i]);
}
return this;
};
dv.css({ width: "100px", height: "100px", backgroundColor: "red" });
dv.on("click", () => {
console.log(1);
});
dv.each(function (i, e) {
e.style.backgroundColor = "yellow";
}
for…in…遍历对象
for(let 变量 in 要遍历的对象)
let a = {
name : "劫",
tips : "瞬狱影杀阵"
}
for(let key in a){
console.log(key,a[key])
}
call,bind,apply
call,bind,apply都是借用方法,都能改变this指向
借用:调用一个不在自身原型链上的方法
call(新this指向,参数1,参数2…)
bind(新this指向,参数1,参数2…) 会返回一个新函数,不调用不执行
apply(新this指向,[参数1,参数2])后面要求数组方式传参
Array.prototype.forEach.call(document.querySelectorAll("input"), (e) => {
e.addEventListener("click", function () {
e.value = "嗯嗯";
});
});
function fun(name) {
console.log(this);
console.log(name);
}
// fun.bind({ id: "aa" }, "我", "你");
var fn = fun.bind({ id: "aa" }, "我", "你");
fn();
function fun() {
let arr = Array.prototype.splice.call(arguments, 0);
let max = Math.max.apply(null, arr);
return max;
}
console.log(fun(1, 2, 5, 4, 9, 6, 8));
class类
es5中写原型和继承比较麻烦 所以es6中新加了一种语法
class Fun{
constructor(name,age){
this.name = name;
this.age = age
}
fn(){
console.log(1)
}
}
let a = new Fun("青钢影",50)
extends继承
class Fun{
constructor(name,age){
this.name = name;
this.age = age
}
fn(){
console.log(1)
}
}
class Ez extends Fun{
constructor(name,age,tips){
// 用来继承父类上的属性
super(name,age);
this.tips = tips;
}
}
let a = new Ez("青钢影",50,"海克斯最后通牒");
a.fn();
箭头函数
箭头函数是es6中新语法,就是一种匿名函数。
语法:(参数1,参数2)=>{函数语句}
let fun = x=>console.log(x);
fun(1);
// 如果只有1个参数或者1条函数语句,那么括号可以省略
箭头函数上的this指向上一级作用域中的this
let fun = ()=>{console.log(this)}
fun();//window
function fun(name) {
this.name = name;
this.fn = () => {
console.log(this);
};
}
let ff = new fun("ha");
ff.fn();// ff
console.log(ff);
且无法直接更改
let fun = ()=>{console.log(this)} // window
fun.call({name:11}) //window
fun.bind({name:1}) // window
fun.apply({name:1}) // window
没有arguments 用…rest代替
let fun = ()=>{
return arguents;
}
console.log(fun(1,2,3,4))// undefined
let fn = (...rest)=>{
return rest;
}
console.log(fn(1,2,3))// [1,2,3]
箭头函数没有原型,不能当构造函数,不能用new
arguments和…rest
arguments:拿到用户输入的参数,是伪数组,不能使用数组的API
…rest:rest是个变量名,…rest拿到的是除设置的形参外多余的参数,是实在的数组,且只能写在设置的形参最后
let fun = (a,...rest,b)={}// 报错
let fn = (...rest)=> {
rest.push(1)
return rest
};
console.log(fn(1)); // [1,1]
this
普通函数指向:window
构造函数指向:实例化对象
回调函数指向:大部分window
事件处理函数:事件源
箭头函数指向:上一级作用域的this
对象的方法指向:实例对象
递归
函数内部自己调用自己
递归遍历dom树
let arr = [];
function fun(parent, cla) {
if (parent.className == "dd") {
arr.push(parent);
}
Array.prototype.forEach.call(parent.children, (e, i) => {
fun(e, cla);
});
}
fun(document, "dd");
console.log(arr);
异常捕获
代码报错会影响后面的执行,有时候一个不重要的地方会报错,可以用异常捕获来获取,且报错后会继续执行代码
try{
可能会报错的代码
}catch(e){
console.log(e); //打印错误信息
}finally{
无论报不报错都会执行的代码
}
严格模式
想要哪里使用严格模式,就在该作用域中添加"use strict"。
严格模式:
1.消除Javascript语法的一些不合理、不严谨之处,减少一些怪异行为;
2.消除代码运行的一些不安全之处,保证代码运行的安全;
3.提高编译器效率,增加运行速度;
严格模式不允许使用未定义的变量
x=1;
严格模式静止对只读属性赋值
let obj = {};
Object.defineProperty(obj, "name", {
value: "我",
});
obj.name = "ni";
不能删除 不可删除属性
let obj = {};
Object.defineProperty(obj, "name", {
value: "我",
});
delete obj.name;
禁止函数参数名重名
(function (a, a, c) {})();
禁止8进制数
let aa = 015;
禁止设置原始类型(primitive)值的属性*(function() {*
(function() {
"use strict";
false.true = ""; //TypeError
(14).sailing = "home"; //TypeError
"with".you = "far away"; //TypeError
})();
禁止with
with (a) {}
禁止使用eval()创建的变量
eval("let a = 0");
console.log(a);
禁止删除变量
delete obj;
禁止使用arguments和eval作为变量名
let arguments = 1;
let eval = 1;
严格模式下,函数的 arguments 对象会保存函数被调用时的原始参数。arguments[i] 的值不会随与之相应的参数的值的改变而变化,同名参数的值也不会随与之相应的 arguments[i] 的值的改变而变化。
function f(a,b) {
a=10;
console.log(arguments[0]);//1
arguments[0] = 20;
console.log(a);//10
}
f(1,2);
不再支持 arguments.callee
var f = function() {
return arguments.callee;
};
f();
禁止this指向全局对象,当this指向全局对象时,自动转为undefined
function a(){
console.log(this)
}
a()
Object.defineProperty设置高级属性
Object.defineProperty(要修改的对象,属性名,{属性值操作})
let obj ={};
Object.defineProperty(obj,"name",{
value : "阿卡丽"; // 设置属性值
writable : true ; // 设置是否可以修改 默认false --- 只读
enumerable:true ; // 设置是否可以遍历 默认false
configurable:true; // 设置是否可以被删除
})
设置器和获取器
set/get 不能和value/writable同时存在
let obj = {};
// 因为名字不能随意被外部更改 所以用闭包
(() => {
let _name;
Object.defineProperty(obj, "name", {
set: function (val) {
if (typeof val == "string") {
// 这里不能用this.name = val 因为这也是个赋值操作,会循环调用set方法产生递归
_name = val;
}
},
get: function () {
// return的值 就是你设置的name的值
return _name;
},
});
})();
obj.name = "13";
console.log(obj);
闭包
一个函数可以访问上一个作用域数据,形成闭包。
闭包可以保护数据,缓存数据
但是闭包保持了对数据的引用,会内存泄漏,且执行效率低
深拷贝与浅拷贝
浅拷贝
浅拷贝会把第一次层的复制一次
let obj = {
a: 1,
b: 2,
c: {
d: 4,
},
};
let obj1 = {};
for (let key in obj) {
obj1[key] = obj[key];
}
obj1.c.d = 11;
console.log(obj);// 由于c.d是第二层 所以不会复制,修改的话会指向原来的地址
console.log(obj1);
深拷贝
深拷贝会把对象的每一层都复制
使用递归
let obj = {
a: 1,
b: 2,
c: {
d: 4,
},
};
// 要复制的新对象要放在里面
function fun(obj) {
let obj1 = {};
for (var key in obj) {
if (typeof obj[key] == "object" && obj[key] != null) {
// 因为这里要二层对象 等于一个新对象 所以要将声明的新对象放在里面循环使用
obj1[key] = fun(obj[key]);
} else {
obj1[key] = obj[key];
}
}
return obj1;
}
let obj1 = fun(obj);
obj1.c.d = 55;
console.log(obj);
console.log(obj1);
JSON转化
let obj = {a:1,b:2,c:{d:4}}
let obj1 = JSON.parse(JSON.stringify(obj))
console.log(obj);
console.log(obj1);
正则
正则是用来检测是否复合指定标准的规则
let a = new RegExp("规则");
let b = /规则/
b.test(要检测的字符串)
//常用的正则
/d 数字 /w 字母数字下划线 /s 空白字符 //大写表示非
^以什么开头 $以什么结尾
*匹配任意次 +至少1次 ? 0次或1次 {n}匹配n次 {n,}匹配至少n次 {n,m}匹配n-m次
[0-9]数字 [a-zA-Z]字母 [任意规则]
[规则]g ---全局匹配 [规则]ig ---忽略大小写的全局匹配
字符串.match(规则) 正则提取
let str4 = "2020-09-65";
let str5 = str4.match(/^\d{4}-(\d{2})-(\d{2})$/);
console.log(str5);
设计模式
单例模式
保证永远只有一个实例
(function () {
let _name;
function fun(name) {
if (_name) {
return _name;
} else {
this.name = name;
_name = this;
}
}
window.fun = fun;
})(window);
let obj = new fun("jinfu");
console.log(obj);
let obj1 = new fun("ruiwen");
console.log(obj1);
订阅发布模式
需要一个人负责管理所有的状态
要有别的对象对这个人事先订阅
let obj = {
add: function (type, fn) {
// 技能
if (!this[type]) {
this[type] = [];
}
this[type].push(fn);
},
// 上buff
tigger: function (type) {
this[type].forEach((e) => {
e();
});
},
};
document.querySelector("input").addEventListener("click", function () {
// 上buff
obj.tigger("login");
});
obj.add("login", function () {
console.log(1);
});
obj.add("login", function () {
console.log(2);
});
obj.add("login", function () {
console.log(3);
});
hasOwnProperty()
检测一个属性是否是一个对象的直接属性
let obj = {
name:"a",
}
console.log(obj.hasOwnProperty("name"));
in关键字
检测属性是否在对象的原型链上
let obj = {
name:"re"
}
console.log("name" in obj);//t
console.log("toString" in obj); // t
console.log("rt" in obj); //f
instanceof
检测一个对象是否是构造函数的实例对象
console.log(arr instanceof Array); // t
console.log(obj instanceof Array); // error
console.log(arr instanceof Object); // t
isPrototypeOf
检测对象是否是另一个的原型对象 ====== 原型对象.isPrototypeOf(实例对象)
function fun(name){
this.name = name
};
let obj = new fun("ren")
console.log(Object.prototype.isPrototypeOf(obj))
filter
数组中一个方法,筛选满足条件的元素,返回一个新数组
// arr.filter((e,i)=>{
// return 条件
// })
let arr = [1,2,3,4]
let res = arr.filter((e,i)=>{
return e>2
});
console.log(res)
find
找到数组满足条件的第一个元素
let arr = [1,2,3,4,5];
let res = arr.find((e,i)=>e>2);
console.log(res);
Object.assin
复制对象,是种浅拷贝
let obj = {
name : "aaa",
o : {
age : 12
}
}
let obj1 = Object.assin({},obj);
console.log(obj,obj1)
with()
将相同的对象放在一起
let dv = document.querySelector("div");
with(dv.style){
width = 12 +"px";
height = 12 + "px"
}
eval()
将满足js语法的字符串执行
eval(`let a = 10 ; console.log(a)`);
Object.create()
Object.create(原型对象,多个属性)
let proto = {
name : '爸爸',
sayHi : function(){
console.log(this.name)
}
}
let son = Object.create(proto,{
age : {
value:10,
writable:true
},
gender:{
value:'男',
writable:true
}
});
console.log(son)
解构
对象解构
let obj ={
name : 66,
age :55
}
let {name,age} = obj
// let name = obj.name
// let name = obj.name
// 起别名用:
let {name:_name,age:_age} =obj
// let _name = obj.name
// let _age = obj.age
数组解构
let arr = [1,2,3,4,5];
let [a,b,c,d] = arr;
let arr1 = [1,[2,3],4];
let [,[,s]] = arr1