前言
本文包含JavaScript高级大致内容(不全部包含ES6新语法),参考与pink老师所讲解视频通过自己理解整理,所以这是一篇用于小白新手入门,或者复习使用的笔记,ES6新语法以后也会更新。
如有做的不好的地方,敬请谅解,欢迎指出,持续更新改正
一 面向对象与类
1 面向对象
面向过程 (POP)
高性能 不易维护
面向对象 (OOP)
性能低 易维护 灵活特点:
- 封装
- 继承
- 多态
类和对象
对象抽象封装成一个类
类实例化获取类的对象
2 创建类与实例化
用class关键字创建类
class Star {
constructor(uname, age) { //new生成实例时,constructor自动调用,不写自动生成
this.uname = uname;
this.age = age;
}
}
利用类创建对象
var ldh = new Star("刘德华", 20);
var zxy = new Star("张学友");
console.log(ldh);
类中添加方法
- 类里面函数不需要写function
- 多个方法间不需要逗号分隔
class Star {
constructor(uname, age) {
this.uname = uname;
this.age = age;
}
say(){
console.log('说')
}
}
var zxy = new Star("张学友");
zxy.say()
3 继承
(就近原则) 实例化输出方法,先在子类找,再到父类找
class Father {
constructor() {}
money() {
console.log(100);
}
}
class Son extends Father {}
var son = new Son();
son.money();
4 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);
son.sum();
super关键字调用父类普通函数
class Father {
say() {
return "我是爸爸";
}
}
class Son extends Father {
say() {
console.log(super.say() + "的儿子");
}
}
var son = new Son();
son.say();
super必须放在子类this之前
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 = x;
this.y = y;
}
subtract() {
console.log(this.x - this.y);
}
}
var son = new Son(3, 1);
son.subtract();
son.sum();
5 总结
类没有变量提升,必须先定义类,才能实例
类里面的公有属性和方法一定要用this
类里this指向问题
constructor 里面this 指向的是创建的实例
方法里的this 指向的是调用者
二 构造函数与原型
1 实例成员和静态成员
实例成员就是构造函数内部通过this添加的成员 uname age就是
实例成员只能通过实例化对象访问
静态成员 是在构造函数本身添加成员
静态成员只能通过构造函数访问
function Star(uname, age) {
this.uname = uname;
this.age = age;
this.sing = function () {
console.log("唱歌");
};
}
var ldh = new Star("刘德华", 18); //实例成员
console.log(ldh.uname);
Star.sex = "男"; //静态成员
console.log(Star.sex);
2 原型
构造函数问题
存在浪费内存问题 函数会开辟不同内存空间
2.1 构造函数原型prototype
通过原型分配的函数是所有对象 共享的
每一个构造函数都有一个prototype属性(对象)
把那些不变的方法,直接定义在prototype对象上,所有对象实例就可以共享这些方法
一般情况下,公共属性定义到构造函数里,公共方法放到原型对象上
function Star(uname, age) {
this.uname = uname;
this.age = age;
}
Star.prototype.sing = function () {
console.log("我会唱歌");
};
var zt = new Star("张铁", 18);
var yy = new Star("鸳鸯", 18);
zt.sing();
yy.sing();
console.log(zt);
console.log(Star);
2.2对象原型 proto
对象身上系统自己添加一个__proto__指向构造函数原型对象
实例对象没有sing 这个方法, 因为有__proto__ 存在, 就去构造函数原型对象prototype找
console.log(zt. __proto__ === Star.prototype);
2.3 constructor
对象原型和原型对象 都有一个属性constructor属性 称为构造函数,因为它指回构造函数本身
主要用于记录该对象引用于哪一个构造函数,可以让原型对象重新指向原来构造函数
如果我们修改了原来的原型对象, 给原型对象赋值的是一个对象,必须手动利用constructor
在用原型对象添加许多方法时,可以利用对象{}形式添加,但是 = 的操作会覆盖constructor,所以需要手动添加
Star.prototype = { //此时不是用 . 而是用 = 覆盖了
constructor: Star, //重新指向
sing: function () {
console.log("我会唱歌");
},
movie: function () {
console.log("我会演电影");
}
};
3 构造函数 , 实例, 原型对象三者之间的关系
Star.prototype
Star构造函数 -------------------------------> Star原型对象
<------------------------------
Star.prototype.constructor
\ /
\ /ldh.__proto__
\ /
ldh对象实例
ldh.__proto__.constructor
4 原型链
每一个对象都有一个对象原型,通过__proto__一层一层向上寻找
原型链查找
- 当访问一个对象属性或方法时,首先查找这个对象自身有没有
- 如果没有,就查找他的原型(__proto__指向的prototype原型对象)
- 如果还没有,就查找原型对象的原型(Object的原型对象)
- 依次类推直到Object为止(null);
- __proto__对象原型存在意义在于为对象成员查找机制提供方向,或者说一条路线
5 继承
ES6之前没有提供extends继承, 通过构造函数+原型对象模拟实现继承,组合继承
fun.call(thisArg,arg1,arg2)
调用这个函数
并且修改函数运行时this指向
- thisArg: 当前调用函数this指向对象
- arg1,arg2: 传递的其他参数
function fn(x, y) {
console.log("测试");
console.log(this);
console.log(x + y);
}
var o = {
name: "andy",
};
fn.call(o, 1, 2);
利用父构造函数继承属性
function Father(uname, age) {
this.uname = uname;
this.age = age;
}
function Son(uname, age, score) {
Father.call(this, uname, age);
this.score = score;
}
var son = new Son("李德华", 18, 100);
console.log(son);
利用原型对象继承方法
Son.prototype = new Father();
//如果利用对象的形式修改了原型对象,利用constructor 指回原来的构造函数
Son.prototype.constructor = Son;
6 类的本质
其实还是一个函数 类就是构造函数的另外一种写法
class Star {}
//类有原型对象
console.log(Star.prototype);
//类原型对象里有constructor 指向本身
console.log(Star.prototype.constructor);
//类可以通过原型对象添加方法
Star.prototype.sing = function () {
console.log("冰雨");
};
var ldh = new Star();
console.dir(ldh);
//类创建的实例对象有__proto__ 原型指向 类的原型对象
console.log(ldh.__proto__ === Star.prototype);
三 ES5新增方法
1 新增数组方法
1.1 forEach() 遍历数组
var arr = [1, 2, 3];
var sum = 0;
arr.forEach(function (value, index, array) {
console.log("每个数组元素" + value);
console.log("每个数组元素索引" + index);
console.log("数组本身" + array);
sum += value;
});
console.log(sum);
1.2 filter() 筛选数组
返回一个新数组
var arr = [12, 3, 5, 66, 88];
var newArr = arr.filter(function (value, index, array) {
return value >= 2;
});
console.log(newArr);
1.3 some() 是否满足条件元素
返回一个布尔值
如果找到第一个满足条件的元素,则终止循环,不在继续查找
var arr = ["red", "pink", "blue"];
var flag = arr.some(function (value) {
return value == "pink";
});
console.log(flag);
1.4 some和forEach区别
forEach 和 filter 里 return 不会终止迭代
some 里 return 就是终止调用
2 新增字符串方法 trim()
会从一个字符串 两端 删除空白字符 , 返回一个新字符串
var str = " andy ";
console.log(str);
var str1 = str.trim();
console.log(str1);
3 ES5新增对象方法
3 1 Object.keys(obj) 获取对象属性名
效果类似于for in 返回由属性名组成的数组
var obj = {
name: "zt",
age: 19,
sex: "男",
};
var arr = Object.keys(obj);
console.log(arr);
arr.forEach(function (value) {
console.log(value);
});
3.2Object.defineProperty() 定义对象中新属性或修改原有属性
Object.defineProperty(obj,prop,descriptor)
- obj: 必须 目标属性
- prop: 必须 需定义或修改的属性名字
- descriptor:
对象格式 必须 目标属性所拥有的特性
- value: 设置属性的值 默认为undefined
- writable: 值是否可以重写 true|false
- enumerable: 目标属性是否可以被枚举 是否可以被遍历 true|false
- configurable: 目标属性是否可以被删除或是否可以被再次修改特性 true|false
四 函数定义与严格模式
1 新函数定义方式
利用new Funcation(‘参数1’,‘参数2’,‘函数体’)
var fn = new Function('a' + 'b' + "console.log(a+b)");
fn(1,2);
所有的函数都是Function 的实例(对象)
函数也属于对象
2 改变函数内部this 指向
2 1 apply()
方法调用一个函数, 也可以改变函数this指向
- fun.apply(thisArg,[argsArray])
- argsArray: 传递的值,必须包含在 数组 里
主要应用
利用apply() 借助于数学内置对象求最大值
var arr = [1, 66, 3, 88, 99];
var max = Math.max.apply(Math, arr);
console.log(max);
2.2 bind()
不会调用函数, 可以改变this指向
返回的是原函数改变 this 指向的新函数
fun.bind(thisArg, arg1, arg2);
var o = {
name: "zt",
};
function fn(a, b) {
console.log(this);
console.log(a + b);
}
var f = fn.bind(o, 1, 2);
f();
如果有的函数我们不需要立即调用,但又想改this指向, 此时用bind
有一个按钮,点击之后,禁用,3s后开启这个按钮
var btn = document.querySelector("button");
btn.onclick = function () {
this.disabled = true;
setTimeout(
function () {
this.disabled = false;
}.bind(this),
3000
);
};
2.3 call()
fun.call(thisArg,arg1,arg2)
调用这个函数
并且修改函数运行时this指向
- thisArg: 当前调用函数this指向对象
- arg1,arg2: 传递的其他参数
3 严格模式
为整个script开启严格模式
下面的js 代码就会按照严格模式执行代码
'use strict'
为函数开启严格模式
function fn() {
"use strict";
}
4 严格模式的变化
变量规定
- 必须先声明再使用
- 不能随意删除已经声明好的变量
this指向问题
- 全局作用域定义的函数中this 是undefined, 不再指向window
- 构造函数不加new 调用, this会报错
函数变化
- 函数不能有重名的参数
- 函数不能在块级作用域内声明
5 高阶函数
是对其他函数操作的函数, 接收函数作为 (参数) 或者将函数作为 (返回值) 输出
函数也是一种数据类型,同样可以作为参数,传递给另一个参数使用. 最经典的是作为回调函数
function fn(a, b, callback) {
console.log(a + b);
callback && callback();
}
fn(1, 2, function () {
console.log("我是最后调用的");
});
五 闭包与递归
1 闭包 closure
指有权访问另一个函数作用域中变量的 函数
一个作用域可以访问到另一个函数内部的局部变量
function fn() {
var num = 100;
function fun() {
console.log(num);
}
fun();
}
fn();
fn外面的作用域可以访问fn 内部的局部变量
function fn() {
var num = 100;
return function () {
console.log(num);
};
}
var f = fn();
f();
闭包的主要作用: 延伸了变量的作用范围
2 闭包应用
点击li输出当前li 索引号
<ul class="nav">
<li>榴莲</li>
<li>臭豆腐</li>
<li>鱼罐头</li>
<li>猪蹄子</li>
</ul>
var lis = document.querySelector("ul").querySelectorAll("li");
for (var i = 0; i < lis.length; i++) {
lis[i].index = i;
lis[i].onclick = function () {
console.log(this.index);
};
}
利用闭包的方式得到当前小li 的索引号
立即执行函数也称为小闭包, 因为里面任意一个函数都可以使用它的i这个变量
for (var i = 0; i < lis.length; i++) {
//利用for循环创建4个立即执行函数
//立即执行函数也称为小闭包, 因为里面任意一个函数都可以使用它的i这个变量
(function (i) {
lis[i].onclick = function () {
console.log(i);
};
})(i);
}
3 递归
如果一个函数内部可以调用其本身,那么这个函数就是递归函数
函数内部自己调用自己,就叫递归函数 作用和循环效果一样
容易发生"栈溢出",必须加退出条件
var num = 1;
function fn() {
console.log("话");
if (num == 6) {
return;
}
num++;
fn();
}
fn();
利用递归函数求1~n 阶乘 1234…n
function fn(n) {
if (n == 1) {
return 1;
}
return n * fn(n - 1);
}
console.log(fn(3));
4 浅拷贝和深拷贝
浅拷贝只是拷贝一层,更深层对象只拷贝引用
Object.assign(目标,源)
深拷贝拷贝多层,每一级别数据都会拷贝
六 正则表达式
js中正则表达式也是对象
验证表单(匹配)
过滤掉内容中的一些敏感词(替换)
获取特定部分(提取)
1 使用
利用RegExp对象来创建
var regexp = new RegExp(/123/);
console.log(regexp);
利用字面量创建
var rg = /123/;
console.log(rg);
测试正则表达式 test() 用于检测是否符合规则,返回true或false
regexObj.test(str) str:检测的文本
2 正则表达式的组成
2.1 边界符
^ 表示以谁开始
$ 表示以谁结束
var rg = /^abc$/;
console.log(rg.test("abc"));
2.2 字符类
[]有一系列字符供选择,只需匹配一个就可
如果[] 里面有^表示取反
[]外面有 ^ 正常表示开头
var rg = /[abc]/; //只要包括a 或 包括b 或 包括c 就行
console.log(rg.test("andy"));
var rg = /^[abc]$/ //三选一,只有是a 或者是b 或者是c
var rg = /^[a-z0-9]$/ //26个英文字母任何一个字母和数字返回 true
2.3 量词符
用来设定某个模式出现的 次数
*相当于 >=0 可以出现0次或者很多次
var rg = /^a*$/;
console.log(rg.test(""));
console.log(rg.test("a"));
console.log(rg.test("aaa"));
+相当于 >=1 可以出现1次或者很多次 不包含0次
? 相当于 1||0 只包括 1次 或 0次
{3 }重复3次 {3,} 大于等于3 {3,16} 大于等于3,小于等于16
2.4 预定义类
某些常见模式的简写方式
- \d 相当于[0-9]
- \D 相当于[^0-9]
- \w 相当于[A-z0-9]
- \W 相当于[^A-z0-9]
- \s 匹配空格等
- \S 匹配非空格
2.5正则替换
replace 替换
正则表达式参数
- g: 全局匹配
- i: 忽略大小写
- gi:都有