一. 面向对象编程介绍
二、ES6中的类和对象
<script>
// 1. 创建 class 创建一个 明星类
class Star { // 类里面所有函数都不需要加function
constructor (uname,age) {
this.uname = uname; // this 指向创建的实例
this.age = age
}
}
// 2. 利用类创建对象 new
var ldh = new Star('刘德华',18);
var zxy = new Star('张学友',20);
console.log(ldh);
console.log(zxy);
// (1) 通过class 关键字创建类,类名我们还是习惯性定义首字母大写
//(2)类里面有个constructor 函数,可以接受传递过来的参数,同时返回实例对象
//(3)constructor 函数 只要 new 生成实例时, 就会自动调用这个函数,如果不写这个函数,类也会自动生成这个函数
//(4)生成实例 new 不能省略
//(5)最后注意语法规范,创建类 类名后面不要加小括号,生成实例 类名后面加小括号,构造函数不需要加function
</script>
<script>
// 1. 创建 class 创建一个 明星类
class Star { // 类里面所有函数都不需要加function
constructor (uname,age) {
this.uname = uname; // this 指向创建的实例
this.age = age
} // 这个不需要逗号
sing (song) {
console.log(song);
}
}
// 2. 利用类创建对象 new
var ldh = new Star('刘德华',18);
var zxy = new Star('张学友',20);
console.log(ldh);
console.log(zxy);
// (1)我们类里面所有的函数不需要写function
// (2)多个函数方法之间不需要添加逗号分隔
ldh.sing('冰雨')
zxy.sing('李香兰')
</script>
三、类的继承
<script>
// 1. 类的继承
// class Father {
// constructor(age) {
// this.age = age
// }
// money() {
// console.log(100);
// }
// }
// class Son extends Father {
// }
// var kan = new Son(18);
// console.log(kan.age);
// kan.money()
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) {
// this.x = x; // 这样写下面不能用,它不知道调用的是父类的构造函数里面的xy还是子类的构造函数的xy
// this.y = y;
super(x,y); //调用了父类中的构造函数
}
}
var son = new Son(1,2)
son.sum()
</script>
class Father {
say() {
return '我是爸爸'
}
}
class Son extends Father {
say() {
// console.log('我是儿子');
console.log(super.say())
// super.say() 就是调用父类中的普通函数 say()
}
}
var son = new Son();
son.say()
// 继承中的属性或方法查找原则: 就近原则
// 1. 继承中,如果实例化子类输出一个方法,先看看子类有没有这个方法,如果有就先执行子类的方法
// 2. 继承中,如果子类没有,就去查找父类有没有这个方法,如果有,就执行父类的这个方法(就近原则)
</script>
<script>
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 调用父类的构造函数
// super 必须在子类this 之前调用
super(x, y);
this.x = x;
this.y = y;
}
subtract() {
console.log(this.x - this.y);
}
}
var son = new Son(5, 3)
// son.subtract()
son.sum()
</script>
<script>
// var ldh = new Star('刘德华') // 这样会报错
var that
var _that
class Star {
constructor(uname,age) {
// constructor 里面的this 指向的是 创建的实例对象
that = this
console.log(this);
this.uname = uname;
this.age = age;
// this.sing(); // 自动调用
this.btn = document.querySelector('button');
this.btn.onclick = this.sing
}
sing () {
// 这个sing 方法里面的this 指向的是 btn 这个按钮,因为这个按钮调用了这个函数
console.log(this);
console.log(this.uname); // 这里必须加this,实例对象的uname
console.log(that.uname);
}
dance() {
// 这个dance里面的this 指向的是实例对象 ldh 因为ldh 调用了这个函数
_that = this
console.log(this);
}
}
var ldh = new Star('刘德华')
console.log( that === ldh);
ldh.dance();
console.log(_that === ldh);
// ldh.sing()
// 1. 在 ES6 中没有变量提升, 所以必须先定义类, 才能通过类实例化对象
// 2. 类里面共有的属性和方法一定要加this使用
</script>
四、面向对象的案例
1. 动态添加tab栏
五、构造函数和原型
// 1. 利用 new Object() 创建对象
var obj1 = new Object();
// 2. 利用字面量创建对象
var obj2 = {};
// 3. 利用构造函数创建对象
function Star(uname, age) {
this.uname = uname;
this.age = age;
this.sing = function () {
console.log('我会唱歌');
}
}
var ldh = new Star('刘德华', 18);
console.log(ldh);
unction Star(uname, age) {
this.uname = uname;
this.age = age;
this.sing = function () {
console.log('我会唱歌');
}
}
var ldh = new Star('刘德华', 18);
// 实例成员就是构造函数内部通过this添加的成员 uname age sing 就是实例成员
// 实例成员只能通过实例化的对象来访问
// console.log(ldh.uname);
// ldh.sing();
console.log(Star.uname); // 不可以通过构造函数来访问实例成员
// 2. 静态成员 在构造函数本身上添加的成员
Star.sex = '男';
console.log(Star.sex);
console.log(ldh.sex); //不能通过对象来访问
console.log(Star);
function Star(uname, age) {
this.uname = uname;
this.age = age;
this.sing = function () {
console.log('我会唱歌');
}
}
var ldh = new Star('刘德华', 18);
var zxy = new Star('张学友', 16);
// console.log(ldh === zxy); // false 地址不同了
// console.log(ldh.sing == zxy.sing); //faalse 这两个对象实例分别存在不同的内存空间了
// var a = {}
// var b = {}
// console.log(a == b); // false
console.dir(Star);
function Star(uname, age) {
this.uname = uname;
this.age = age;
// this.sing = function () {
// console.log('我会唱歌');
// }
}
Star.prototype.sing = function () {
console.log('我会唱歌');
}
var ldh = new Star('刘德华', 18);
var zxy = new Star('张学友', 16);
console.log(ldh.sing === zxy.sing); // 同一个地址,方法共享
// console.dir(Star);
// console.log(ldh);
ldh.sing()
zxy.sing()
// 2. 一般情况下,我们的公共属性定义到构造函数里面,公共方法我们放到原型对象身上
function Star(uname, age) {
this.uname = uname;
this.age = age;
}
Star.prototype.sing = function () {
console.log('我会唱歌');
}
var ldh = new Star('刘德华', 18);
var zxy = new Star('张学友', 16);
// ldh.sing = function () {
// console.log('我不会唱歌');
// }
ldh.sing(); //我不会唱歌
zxy.sing() // 我会唱歌
console.dir(Star);
console.dir(ldh); // 对象身上系统自己添加一个 _proto_ 指向我们构造函数的原型对象 prototype
console.log(ldh.__proto__ === Star.prototype); //true
console.log(ldh.__proto__);
// 方法的查找规则: 首先看ldh 对象身上是否有 sing 方法,如果有就执行这个对象上的sing
// 如果没有sing 这个方法,因为__proto__的存在,就去构造函数原型对象prototype身上取查找
function Star(uname, age) {
this.uname = uname;
this.age = age;
}
// 很多情况下,我们需要手动的利用constructor 这个属性指回 原来的构造函数
// Star.prototype.sing = function () {
// console.log('我会唱歌');
// }
// Stra.prototype.movie = function () {
// console.log('我会演电影');
// }
Star.prototype = {
// 如果我们修改了原来的原型对象,给原来的原型对象赋值的是一个对象,
//则必须手动利用constructor指回原来的构造函数
constructor:Star,
sing:function() {
console.log('我会唱歌');
},
movie:function() {
console.log('我会演电影');
}
}
var ldh = new Star('刘德华', 18);
var zxy = new Star('张学友', 16);
console.log(Star.prototype);
console.log(ldh.__proto__);
console.dir(Star.prototype.constructor);
console.log(ldh.__proto__.constructor);
function Star (uname,age) {
this.uname = uname;
this.age = age
}
Star.prototype.sing = function () {
console.log('我会唱歌');
}
var ldh = new Star('刘德华',18)
// 1. 只要是对象就有__proto__原型,指向原型对象
console.log(Star.prototype);
console.log(Star.prototype.__proto__ === Object.prototype); //true
console.log(Object.prototype.__proto__); // null
function Star(uname, age) {
this.uname = uname;
this.age = age;
}
Star.prototype.sing = function () {
console.log('唱歌');
}
var ldh = new Star('刘德华', 18);
Object.prototype.sex = '男';
Star.prototype.sex = '女';
ldh.sex = '男性';
console.log(ldh.sex);
console.log(Object.prototype); // 这里有toString()方法
console.log(Star.prototype); // 这里没有toString()方法
console.log(ldh); // 这里没有toString()方法
console.log(ldh.toString());
function Star(uname, age) {
this.uname = uname;
this.age = age;
}
var that ;
Star.prototype.sing = function () {
console.log('唱歌');
that = this
}
var ldh = new Star('刘德华',18)
// 1. 在构造函数中里面this指向的是对象实例 ldh
ldh.sing();
console.log(that === ldh);
// 2.原型对象函数里面的this,指向的对象的实例
// 原型对象的应用给 拓展内置对象方法
Array.prototype.sum = function () {
var sum = 0;
for (var i = 0; i < this.length; i++) {
sum += this[i]
}
return sum;
}
// 这样回覆盖原来的prototype 而且回报错!不能这样写
// Array.prototype = {
// sum: function () {
// for (var i = 0; i < this.length; i++) {
// sum += this[i]
// }
// return sum;
// }
// }
console.log(Array.prototype);
var arr = [1,2,3];
console.log(arr.sum());
var arr1 = new Array(11,22,33)
console.log(arr1.sum());
六、继承
function fn(x, y) {
console.log('我想喝水手磨咖啡');
console.log(this);
console.log(x + y);
}
var o = {
name: 'andy'
}
// fn()
// 1. call() 可以调用函数
fn.call()
// 2. call() 可以改变这个函数的this指向 此时这个函数的this 就指向了o这个对象
// fn.call(o,3,2);
// 借用父构造函数继承属性
// 1. 父构造函数
function Father(uname,age) {
// this 指向父构造函数的对象实例
this.uname = uname;
this.age = age;
}
// 2. 子构造函数
function Son (uname,age,score) {
// this指向子构造函数的对象实例
Father.call(this,uname,age)
this.score = score;
}
var son = new Son('刘德华',18,100);
console.log(son);
// 借用父构造函数继承属性
// 1. 父构造函数
function Father(uname,age) {
// this 指向父构造函数的对象实例
this.uname = uname;
this.age = age;
}
Father.prototype.money = function () {
console.log(100000);
}
// 2. 子构造函数
function Son (uname,age,score) {
// this指向子构造函数的对象实例
Father.call(this,uname,age)
this.score = score;
}
//Son.prototype = Father.prototype; // 引用类型赋值,指针都指向了同一块内存了,如果修改了子原型对象,父原型对象也会跟着一起变化
Son.prototype = new Father();
// 如果利用对象的形式修改了原型对象,别忘了利用constructor 指回原来的构造函数
Son.prototype.constructor = Son;
// 这是子构造函数专门的方法
Son.prototype.exam = function () {
console.log('孩子要考试');
}
var son = new Son('刘德华',18,100);
console.log(son);
console.log( Father.prototype); // 这里也被添加了exam()方法了
console.log(Son.prototype.constructor);
// ES6 之前通过 构造函数+ 原型实现面向对象 编程
//(1)构造函数有原型对象prototype
//(2)构造函数原型对象prototype 里面有constructor 指向构造函数本身
//(3)构造函数可以通过原型对象添加方法
//(4)构造函数创建的实例对象有__proto__ 原型指向 构造函数的原型对象
// ES6 通过 类 实现 面向对象编程
class Star {
}
console.log(typeof Star);
// 1. 类的本质其实还是一个函数 我们课可以简单认为 类就是 构造函数的另一种写法
//(1)类有原型对象prototype
console.log(Star.prototype);
//(2)类原型对象prototype 里面有constructor 指向类本身
console.log(Star.prototype.constructor);
//(3)类可以通过原型对象添加方法
Star.prototype.sing = function() {
console.log("冰雨");
}
//(4)类创建的实例对象有__pro__ 原型指向 类的原型对象
var ldh = new Star();
console.log(ldh);
console.log(ldh.__proto__ === Star.prototype);
七、ES5 中的新增方法
// forEach 迭代(遍历)数组
var arr = [1,2,3];
var sum = 0
arr.forEach(function(value,index,arr){
console.log('每个数组元素'+value);
console.log('每个数组元素的索引号'+index);
console.log('数组本身'+arr);
sum += value;
// sum += arr[index]
})
console.log(sum);
// filter 筛选数组
var arr = [12, 66, 4, 88,3,7]
var newArr = arr.filter(function (value, index) {
// return value >= 20;
return value %2 === 0;
})
console.log(newArr);
console.log(arr);
// some 查找数组中是否有满足条件的元素
var arr = [12, 66, 4, 88, 3, 7]
var result = arr.some(function (value) {
return value > 77
})
console.log(result);
3.4对象方法
// 用于获取对象自身所有的属性
var obj = {
id: 1,
pname: '小米',
price: 1999,
num: 2000
};
var arr = Object.keys(obj);
console.log(arr);
arr.forEach(function(value) {
console.log(value);
})
// Object.defineProperty() 定义新属性或修改原有的属性
var obj = {
id:1,
pname:'小米',
price:1999
};
// 1. 以前的对象添加属性和修改属性的方式
// obj.name = 999;
// obj.price = 10
// console.log(obj);
// 2. Object.defineProperty() 定义新属性或修改原有的属性
Object.defineProperty(obj,'num',{
value:1000
})
console.log(obj);
Object.defineProperty(obj,'price',{
value:99
})
console.log(obj);
Object.defineProperty(obj,'id',{
// 如果值false 不允许修改这个属性值 默认值也是false
writable:false
})
obj.id = 10
console.log(obj);
Object.defineProperty(obj,'address',{
value:'中国山东找蓝翔',
writable:false,
enumerable:false, // 默认为false 如果值为false 则不允许遍历
configurable:false // 如果为false 则不允许删除这个属性,还不允许再修改第三个参数里面的特性 默认为false
})
console.log(obj);
var arr = Object.keys(obj)
console.log(arr); // id pname price
delete obj.address
delete obj.pname //
console.log(obj); // address属性还在
八、 函数进阶
1.函数的定义和调用
// 函数的定义方式
// 1. 自定义函数(命名函数)函数声明
function fn() {}
// 2. 函数表达式(匿名函数)
var fun = function() {}
// 3. 利用 new Function('参数一','参数二','函数体');\
var f = new Function('a','b','console.log(a+b)');
f(1,2);
// 4. 所有函数都是 Function 对象实例
console.dir(f);
console.log(f instanceof Object); // 基本类型用 typeof返回类型
// 函数的调用方式
// 1. 普通函数
function fn() {
console.log('人生的巅峰');
}
// fn() fn.call()
// 2. 对象的方法
var o = {
sayHi: function () {
console.log('人生的巅峰');
}
}
o.sayHi();
// 3. 构造函数
function Star() {};
new Star();
// 4. 绑定事件函数
btn.onclick = function() {}; // 点击了按钮就可以调用这个函数
// 5. 定时器函数
setInterval(function() {console.log(this);},1000) //这个函数是定时器自动1秒钟调用一次,window全局对象调用
// 6. 立即执行函数
(function() {
console.log(this);
})(); // 立即执行函数是自动调用,window全局对象调用
2.this
// 函数的不同调用方式决定了 this 的指向不同
// 1. 普通函数 this 指向window
function fn() {
console.log('普通函数的this' + this);
}
//fn()//== window.fn() //fn.call()
// 2. 对象的方法 this 指向的是对象 o
var o = {
sayHi: function () {
console.log('对象的方法的this'+this);
}
}
o.sayHi();
// 3. 构造函数 thisi 指向ldh 这个实例对象 原型对象里面的this 指向也是 ldh 这个实例对象 (方法的调用者)
function Star() { };
Star.prototype.sing = function () {
}
var ldh = new Star();
// 4. 绑定事件函数 this 指向的是btn 按钮对象
btn.onclick = function () { }; // 点击了按钮就可以调用这个函数
// 5. 定时器函数 this 指向 window
window.setInterval(function () { console.log(this); }, 1000) //这个函数是定时器自动1秒钟调用一次,window全局对象调用
// 6. 立即执行函数 指向 window
(function () {
console.log(this);
})(); // 立即执行函数是自动调用,window全局对象调用
// 改变函数内this指向 js提供了三种方法 call() apply() bind()
// 1. call()
o = {
name: 'andy'
}
function fn(a,b) {
console.log(this);
console.log(a+b);
}
fn.call(o,1,2);
// call 第一个可以调用函数 第二个可以改变函数内的this 指向
// call 的主要作用可以实现继承
function Father(uname,age,sex) {
this.uname = uname;
this.age = age;
this.sex = sex;
}
function Son(uname,age,sex) {
Father.call(this,uname,age.sex)
}
var son = new Son('刘德华',18,'男')
// 改变函数内this指向 js提供了三种方法 call() apply() bind()
// 2. apply() 应用 运用 的意思
var o = {
name:'andy'
};
function fn (arr) {
console.log(this);
console.log(arr); // 'pink'
}
fn.apply(o,['pink']);
// 1. 也是调用函数 第二个可以改变函数内部的this指向
// 2. 但是它的参数必须是数组(伪数组)
// 3. apply 的主要应用 比如我们可以利用 apply 借助数学内置对象求最大值
// Math.max();
var arr = [1,66,3,99,4];
var max = Math.max.apply(Math,arr);
// var min = Math.min(...arr)
var min = Math.min.apply(Math,arr)
console.log(max);
console.log(min);
// 3. bind() 绑定 捆绑 的意思、
var o = {
name:'andy'
}
function fn(a,b) {
console.log(this);
console.log(a+b);
}
var result = fn.bind(o,1,2) // 返回了一个函数的拷贝
console.log(result);
result() // {name:'andy'} 3
// 1. 不会调用原来的函数 可以改变原来函数内部的this 指向
// 2. 返回的是原函数改变this 指向后产生的新函数
bind()的使用场景、tab案例的改进
// 3. bind() 绑定 捆绑 的意思、
var o = {
name: 'andy'
}
function fn(a, b) {
console.log(this);
console.log(a + b);
}
var result = fn.bind(o, 1, 2) // 返回了一个函数的拷贝
console.log(result);
result() // {name:'andy'} 3
// 1. 不会调用原来的函数 可以改变原来函数内部的this 指向
// 2. 返回的是原函数改变this 指向后产生的新函数
// 3. 如果有的函数我们不需要立即调用,但是又想改变这个函数内部的this指向此时用bind
// 4. 我们有一个按钮,当我们点击了之后,就禁用这个按钮,3秒钟之后开启这个按钮
// var btn = document.querySelector('button')
// btn.onclick = function () {
// this.disabled = true
// // setTimeout(function(){
// // this.disabled = false //这样不行
// // console.log(this); //这里指向window
// // },3000)
// // 方法1 箭头函数的 this 会寻找最近的外层作用域,该作用域的this 指向谁,箭头函数的this 就指向谁
// // setTimeout(() =>{
// // this.disable = false;
// // },3000)
// // 方法2
// // var that = this;
// // setTimeout(() => {
// // that.disable = false;
// // }, 3000)
// // 方法3
// // setTimeout(function(){
// // this.disabled =false
// // }.bind(this),3000)
// }
var btns = document.querySelectorAll('button')
for (var i = 0; i < btns.length; i++) {
btns[i].onclick = function () {
console.log(this);
this.disabled = true
setTimeout(function () {
this.disabled = false
}.bind(this), 3000)
}
}
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-e1t9XfJF-1628410842310)(C:\Users\KAN\AppData\Roaming\Typora\typora-user-images\image-20210730152755492.png)]
3.严格模式
<!-- 为整个脚本(javascript标签)开启严格模式 -->
<script>
'use strict';
// 下面的js 代码就会按照严格模式执行代码
</script>
<script>
(function() {
'use strict';
// 下面的js 代码就会按照严格模式执行代码
})();
</script>
<!-- 为某个函数开启严格模式 -->
<script>
function fn() {
'use strict';
// 下面的js 代码就会按照严格模式执行代码
}
function fun() {
// 下面的js 代码就会按照普通模式执行代码
}
</script>
// 'use strict'
// 1. 我们的变量名必须先声明再使用
// num = 1;
// console.log(num);
var num = 10;
console.log(num);
// 2. 我们不能随意删除声明好的变量
// delete num;
// 3. 在严格模式下全局作用域中函数中的this 是 undefined
// function fn() {
// console.log(this);
// }
// fn()
// 4. 严格模式下,如果 构造函数不加new调用,this指向undefined 赋值会报错
function Star() {
this.sex = '男'
}
// Star()
// console.log(window.sex);
// var ldh = new Star()
// 5. 定时器中的this 还是指向window
setTimeout(function() {
console.log(this);
},1000)
// 6. 严格模式下不允许函数的参数重名
// a = 1;
// a = 2;
function fn(a,a) {
console.log(a+a);
}
fn(1,2)
4.高阶函数
<style>
div {
position: absolute;
width: 100px;
height: 100px;
background-color: pink;
}
</style>
</head>
<body>
<div></div>
<script>
// 高阶函数- 函数可以作为参数传递
function fn(a, b, callback) {
console.log(a + b);
callback && callback();
}
fn(1, 2, function() {
console.log('我是最后调用的');
});
$("div").animate({
left: 1000
},2000, function() {
$("div").css("backgroundColor", "purple");
})
</script>
5.闭包
// 闭包:(closure)指有权访问另一个函数作用域中的变量的函数
// 闭包: 我们fun 这个函数的作用域 访问了另外一个函数 fn 里面的局部变量 num
// 我们fn 外面的作用域可以访问fn 内部的局部变量
// 闭包的主要作用:延申了变量的作用范围
function fn() {
var num = 10; //num 不会在fn执行完就销毁,它还被子函数引用着
// function fun() {
// console.log(num);
// }
// // fun()
// return fun
return function(){
console.log(num);
}
}
var f = fn()
f();
// 类似于
// var f = function() {
// console.log(num);
// }
<ul class="nav">
<li>1</li>
<li>2</li>
<li>3</li>
<li>4</li>
<li>5</li>
</ul>
<script>
// 闭包应用-点击li输出当前li的索引号
// 1. 我们可以利用动态添加属性的方式
var lis = document.querySelector('.nav').querySelectorAll('li');
for(var i = 0;i<lis.length;i++) {
lis[i].index = i;
lis[i].onclick = function () {
// console.log(i);
console.log(this.index);
}
}
// 2. 利用闭包的方式得到当前小 li 的索引号
for (var i = 0;i<lis.length;i++) {
// 立即执行函数也称为小闭包因为立即执行函数里面的任何一个函数都可以使用它的i这个变量
(function(i){ //容易内存泄漏 ,如果不点击 ,那么i 一直不销毁
// console.log(i);
lis[i].onclick = function () {
console.log(i);
}
})(i)
}
// 3. 用let
</script>
<body>
<ul class="nav">
<li>1</li>
<li>2</li>
<li>3</li>
<li>4</li>
<li>5</li>
</ul>
<script>
// 闭包应用-3秒钟后,打印所哟li元素的内容
var lis = document.querySelector('.nav').querySelectorAll('li');
for (var i = 0; i < lis.length; i++) {
(function (i) {
setTimeout(function () {
console.log(lis[i].innerHTML);
},i*3000)
})(i)
}
</script>
</body>
// 闭包应用-计算打车价格
// 打车起步价13(3公里内),之后每多一公里增加 5块钱,用户输入公里数就可以计算打车价格
// 如果有拥堵情况,总价多收取10块钱拥堵费用
// function fn() {}
// fn()
var car = (function() {
var star = 13; // 起步价 局部变量
var total = 0; // 总价格 局部变量
return {
//正常的总价
price: function(n) {
if (n <= 3) {
total = star;
} else {
total = (n - 3)*5 + star
}
return total
},
//拥堵之后的费用
yd:function(flag){
return flag? total+ 10 : total;
}
}
})()
console.log(car.price(5));
console.log(car.yd(true));
console.log(car.price(1));
console.log(car.yd(false));
闭包思考题
// 思考题 1:
var name = 'The Window';
var object = {
name :'My Object',
getNameFunc: function() {
return function () {
return this.name;
}
}
}
console.log(object.getNameFunc()()); // The Window 没有闭包产生
// var f = object.getNameFunc();
// // 类似于
// var f = function() {
// return this.name;
// }
// f();
// 思考题 2;
var name = 'The Window';
var object = {
name:'My Object',
getNameFunc: function() {
var that = this;
return function () {
return that.name;
}
}
}
console.log(object.getNameFunc()()); //My Object 有闭包产生 that被引用
// var f = object.getNameFunc();
// var f = function() {
// return that.name
// };
// f()
6.递归
// 利用递归求 1~n 的阶乘
function fn(n) {
if (n == 1) {
return 1
}
return n * fn(n-1)
}
console.log(fn(3))
// 详细思路
// return 3 * fn(2)
// return 3 * (2 * fn(1))
// return 3 * (2 * 1)
// 利用递归函数求斐波那契数列(兔子序列) 1、1、2、3、5、8、13、21...
// 用户输入一个数字 n 就可以求出 这个数字对应的兔子序列值
// 我们只需要知道用户输入的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(44));
,
这里原来有BUG
var data = [{
id: 1,
name: '家电',
goods: [{
id: 11,
gname: '冰箱',
goods: [{
id: 111,
gname: '海尔'
}, {
id: 112,
gname: '美的'
},]
}, {
id: 12,
gname: '洗衣机',
goods: [
{
id: 113,
name: '格力'
},
{
id: 114,
name: '耐克'
}
]
}]
}, {
id: 2,
name: '服饰'
}];
// 我们想要输入 id 号,就可以返回数据对象
// 1. 利用 forEach 去遍历里面的每一个对象
var o = {}
function getID(json, id) {
json.forEach(function (item) {
if (item.id == id) {
// forEach return 不会终止迭代
// return item
o = item;
// 2. 我们想要得到第二次的数据
// 里面应该有goods数组 并且数组长度不为0
} else if (item.goods && item.goods.length > 0) {
o = getID(item.goods, id) // 递归内层的 o 和外层的 o 是不同的o 所以需要把内层的o赋值给万层的o
}
});
return o
}
console.log(getID(data, 1));
console.log(getID(data, 111));
// 浅拷贝只是拷贝一层,更深层次对象级别的只拷贝引用。
// 深拷贝拷贝多层, 每一级别的数据都会拷贝。
var obj = {
id :1,
name: 'andy',
msg:{
sex:'男'
}
};
var o = {};
// for(var k in obj) {
// // k 是属性名 obj[k] 属性值
// o[k] = obj[k]; // 注意不能用o.k
// }
// obj.msg.sex = '女'
// console.log(o);
// 语法糖方式实现浅拷贝
Object.assign(o,obj)
obj.msg.sex = '女';
console.log(o);
// 深拷贝拷贝多层, 每一级别的数据都会拷贝。
var obj = {
id: 1,
name: 'andy',
msg: {
sex: '男'
},
color:['pink','green']
};
var o = {};
// 封装函数
function deepCopy(newObj,oldObj) {
for(var k in oldObj) {
// 判断我们的属性值属于哪种数据类型
// 1. 获取属性值 oldObj[k]
var item = oldObj[k];
// 2.判断这个值是否是数组
if (item instanceof Array) {
newObj[k] = [];
deepCopy(newObj[k],item)
} else if (item instanceof Object) {
// 3. 判断这个值是否是对象
newObj[k] = {};
deepCopy(newObj[k],item)
} else {
// 4. 属于简单数据类型
newObj[k] = item;
}
}
}
deepCopy(o,obj)
obj.msg.sex = '女'
console.log(o);
var arr = [];
console.log(arr instanceof Object);
九、正则表达式
1.正则表达式的概述
2.正则表达式在JavaScript中的使用
// 正则表达式在js中的使用
// 1. 利用 RegExp对象来创建 正则表达式
var regexp = new RegExp(/123/);
console.log(regexp);
// 2. 利用字面量创建
var rg = /123/;
// 3. test 方式用来检测字符串是否符合正则表达式要求的规范
console.log(rg.test(123));
console.log(rg.test('abc'));
3.正则表达式中的特殊字符
// 边界符 ^ $
var rg = /abc/; // 正则表达式里面不需要加引号 不管是数字型还是字符型
// /abc/ 只要包含有abc这个字符串返回的都是true
console.log('abc');
console.log(rg.test('abcd'));
console.log(rg.test('aabcd'));
console.log('-----------------------');
var reg = /^abc/;
// 只要是以abc 开头就返回true
console.log(reg.test('abc')); // true
console.log(reg.test('abcd')); //true
console.log(reg.test('aabcd')); // false
console.log('-----------------------');
var reg1 = /^abc$/;
// 精确匹配 要求必须是 abc 字符串才符合规则
console.log(reg1.test('abc')); // true
console.log(reg1.test('abcd')); //false
console.log(reg1.test('aabcd')); // false
console.log(reg1.test('abcabc')); // false
// var rg = /abc/; 只要包含abc就可以
// 字符类:[] 表示有一系列字符可供选择,只要是匹配其中一个就可以了
var rg = /[abc]/; // 只要包含有 a 或者 包含有 b 或者包含有 c 都返回true
console.log(rg.test('andy'));
console.log(rg.test('red'));
var rg1 = /^[abc]$/; // 三选一 只有是 a 或者是 b 或者是 c 这三个字母才返回 true
console.log(rg1.test('aa')); // false
console.log(rg1.test('bb')); // false
console.log('---------------');
var rg2 = /^[a-z]$/; // 26 个英文字母任何一个字母返回 true
console.log(rg2.test('a'));
console.log(rg2.test('z'));
// 字符组合
var reg = /^[a-zA-Z0-9_-]$/; // 26个英文字母任何一个字母返回true - 表示a 到 z 的范围
console.log(reg.test('a'));
console.log(reg.test('B'));
console.log(reg.test('_'));
console.log(reg.test('-'));
console.log(reg.test('!'));
console.log('--------------------------');
// 如果中括号里面有^ 表示取反的意思 千万和 我们的 边界符号 ^ 别混淆
var reg2 = /^[^a-zA-Z0-9_-]$/
console.log(reg2.test('a'));
console.log(reg2.test('B'));
console.log(reg2.test('_'));
console.log(reg2.test('-'));
console.log(reg2.test('!'));
// 量词符: 用来设定某个模式出现的次数
// 简单理解:就是让下面的a这个字符重复多少次
// var reg = /^a$/;
// * 相当于 >= 0 可与出现0次或者很多次
// var reg = /^a*$/;
// console.log(reg.test('')); //true
// console.log(reg.test('a')); //true
// console.log(reg.test('aa')); //true
// console.log(reg.test('b')); //false
// + 相当于 >=1 可以出现1次或者很多次
// var reg = /^a+$/;
// console.log(reg.test('')); //false
// console.log(reg.test('a')); //true
// console.log(reg.test('aa')); //true
// console.log(reg.test('b')); //false
// ? 相当于 1 || 0
// var reg = /^a?$/;
// console.log(reg.test('')); //true
// console.log(reg.test('a')); //true
// console.log(reg.test('aa')); //false
// console.log(reg.test('b')); //false
// {3 } 就是重复3次
// var reg = /^a{3}$/;
// console.log(reg.test('')); //true
// console.log(reg.test('a')); //true
// console.log(reg.test('aaa')); //false
// console.log(reg.test('b')); //false
// {3, } 大于等于3
// var reg = /^a{3,}$/;
// console.log(reg.test('')); //true
// console.log(reg.test('a')); //true
// console.log(reg.test('aaa')); //false
// console.log(reg.test('aaaa')); //true
// {3, 6} // 大于等于3 并且 小于等于6
var reg = /^a{3,6}$/;
console.log(reg.test('')); //true
console.log(reg.test('a')); //true
console.log(reg.test('aaa')); //false
console.log(reg.test('aaaaaaa')); //false
// 量词是设定某个模式出现的次数
var reg = /^[a-zA-Z0-9_-]{6,16}$/; // 这个模式用户只能输入英文字母 数字下划线 短横线 但是有边界符和[] 这就限定了只能多选1
// //{6,16} 中间不要有空格
// console.log(reg.test('aa')); // false
// console.log(reg.test('11')); // false
// console.log(reg.test('1')); // true
// console.log(reg.test('1123456')); // true
// console.log(reg.test('1aBassaa')); // true
// console.log(reg.test('1BAFC---')); // true
// console.log(reg.test('1______--')); // true
// console.log(reg.test('andy!hh')); // true
var uname = document.querySelector('.uname');
var span = document.querySelector('span')
uname.onblur = function() {
if (reg.test(this.value)) {
// console.log('正确');
span.innerHTML = '用户名输入正确';
span.className = 'right'
} else {
span.innerHTML = '用户名输入错误';
span.className = 'wrong'
}
}
// 中括号 字符合集.匹配方括号中的任意字符
var reg =/^[abc]$/;
// a 也可以 b 也可以 c 也可以
// 大括号 量词符.里面重复次数
var reg = /^abc{3}$/; // 他只是让c重复3次
console.log(reg.test('abc')); // false
console.log(reg.test('abcabcabc')); //false
console.log(reg.test('abccc')); // false
// 小括号 表示优先级
var reg = /^(abc){3}$/; //他只是让abc重复三次
console.log(reg.test('abc')); // false
console.log(reg.test('abcabcabc')); //true
console.log(reg.test('abccc')); // false
// 座机号码验证: 全国座机号码两种格式: 010-12345678 0530-1234567
// 正则里面的或者 符号
var reg = /^\d{3}-\d{8}|\d{4}-\d{7}$/ ;
// var reg = /^\d{3,4}-\d{7,8}$/ //这个不行 1111-12345678
注册页手机号码验证
window.onload = function () {
var regtel = /^(13[0-9]|14[5|7]|15[0|1|2|3|4|5|6|7|8|9]|18[0|1|2|3|5|6|7|8|9])\d{8}$/ //手机号码正则表达式
var regqq = /[1-9][0-9]{4,}/;
var tel = document.querySelector('#tel');
var qq = document.querySelector('#qq')
tel.onblur = function () {
if (regtel.test(this.value)) {
console.log(true);
this.nextElementSibling.className = 'success';
this.nextElementSibling.innerHTML ='<i class="success_icon"></i>输入正确'
} else {
this.nextElementSibling.className = 'error';
this.nextElementSibling.innerHTML ='<i class="error_icon"></i>手机号码格式不正确'
}
}
qq.onblur = function () {
if (regqq.test(this.value)) {
console.log(true);
this.nextElementSibling.className = 'success';
this.nextElementSibling.innerHTML ='<i class="success_icon"></i>输入正确'
} else {
this.nextElementSibling.className = 'error';
this.nextElementSibling.innerHTML ='<i class="error_icon"></i>QQ号码格式不正确'
}
}
}
4.正则表达式中的替换
<textarea name="" id="message" cols="30" rows="10"></textarea><button>提交</button>
<div></div>
<script>
// // 替换 replace
// var str = 'andy和red';
// // var newStr = str.replace('andy','baby');
// var newStr = str.replace(/andy/,'baby');
// console.log(newStr);
var text = document.querySelector('#message');
var btn = document.querySelector('button');
var div = document.querySelector('div')
btn.onclick = function() {
div.innerHTML = text.value.replace(/激情|gay/g,'**');
}
十、ES6(部分)
1.let
/*
let关键字就是用来声明变量的
使用let关键字声明的变量具有块级作用域
在一个大括号中 使用let关键字声明的变量才具有块级作用域 var关键字是不具备这个特点的
防止循环变量变成全局变量
使用let关键字声明的变量没有变量提升
使用let关键字声明的变量具有暂时性死区特性
*/
/* --------let关键字就是用来声明变量的-------- */
// let a = 10;
// console.log(a);
/* --------使用let关键字声明的变量具有块级作用域-------- */
// if (true) {
// let b = 20;
// console.log(b)
// if (true) {
// let c = 30;
// }
// console.log(c);
// }
// console.log(b)
/* -------在一个大括号中 使用let关键字声明的变量才具有块级作用域 var关键字是不具备这个特点的--------- */
// if (true) {
// let num = 100;
// var abc = 200;
// }
// console.log(abc);
// console.log(num)
/* -------防止循环变量变成全局变量--------- */
// for (let i = 0; i < 2; i++) {}
// console.log(i);
/*-----使用let关键字声明的变量没有变量提升------*/
console.log(a);
let a = 100;
/* -------使用let关键字声明的变量具有暂时性死区特性------- */
var num = 10
if (true) {
console.log(num); //报错,这个num会和这个块级作用域绑定
let num = 20;
}
2.经典面试题
let arr = [];
for (let i = 0; i < 2; i++) {
arr[i] = function () {
console.log(i);
}
}
arr[0](); // 2
arr[1](); // 2
3.const关键字
// 使用const关键字声明的常量具有块级作用域
// if (true) {
// const a = 10;
// if (true) {
// const a = 20;
// console.log(a);
// }
// console.log(a);
// }
// console.log(a);
// 使用const关键字声明的常量必须赋初始值
// const PI = 3.14;
// 常量声明后值不可更改
const PI = 3.14;
// PI = 100;
const ary = [100, 200];
ary[0] = 123;
ary = [1, 2]
console.log(ary);
4.结构赋值
// 数组解构允许我们按照一一对应的关系从数组中提取值 然后将值赋值给变量
let ary = [1,2,3];
let [a, b, c, d, e] = ary;
console.log(a)
console.log(b)
console.log(c)
console.log(d)
console.log(e)
// 对象解构允许我们使用变量的名字匹配对象的属性 匹配成功 将对象属性的值赋值给变量
// let person = {name: 'lisi', age: 30, sex: '男'};
let person = {name: {size:1}, age: 30, sex: '男'};
// let { name, age, sex } = person;
// console.log(name)
// console.log(age)
// console.log(sex)
let {name: myName} = person;
person.name = '张三'
console.log(myName) // '李四'
5.箭头函数
// 箭头函数是用来简化函数定义语法的
// const fn = () => {
// console.log(123)
// }
// fn();
// 在箭头函数中 如果函数体中只有一句代码 并且代码的执行结果就是函数的返回值 函数体大括号可以省略
// const sum = (n1, n2) => n1 + n2;
// const result = sum(10, 20);
// console.log(result)
// 在箭头函数中 如果形参只有一个 形参外侧的小括号也是可以省略的
// const fn = v => { // 这里可以省略{}
// alert(v);
// }
// fn(20)
// 箭头函数不绑定this 箭头函数没有自己的this关键字 如果在箭头函数中使用this this关键字将指向箭头函数定义位置中的this
function fn () {
console.log(this);
return () => {
console.log(this)
}
}
// fn() // window
// fn()() //window
const obj = {name: 'zhangsan'};
const resFn = fn.call(obj); //obj
resFn(); // 向最近的外层作用域寻找this obj
6.箭头函数面试题
var age = 100;
var obj = { // obj是对象没有作用域,所以不能产生this
age: 20,
say: () => {
alert(this.age)
},
speack:function(){
alert(this.age)
}
}
// obj.say(); // 100
obj.speack() // 100
7.剩余参数
// const sum = (...args) => {
// let total = 0;
// args.forEach(item => total += item);
// return total;
// };
// console.log(sum(10, 20));
// console.log(sum(10, 20, 30));
let ary1 = ['张三' , '李四', '王五'];
let [s1, ...s2] = ary1;
console.log(s1)
console.log(s2)
8.拓展运算符
<body>
<div>1</div>
<div>4</div>
<div>3</div>
<div>6</div>
<div>2</div>
<div>5</div>
<script type="text/javascript">
// 扩展运算符可以将数组拆分成以逗号分隔的参数序列
// let ary = ["a", "b", "c"];
// ...ary // "a", "b", "c"
// console.log(...ary)
// console.log("a", "b", "c")
// 扩展运算符应用于数组合并
// let ary1 = [1, 2, 3];
// let ary2 = [4, 5, 6];
// // ...ary1 // 1, 2, 3
// // ...ary1 // 4, 5, 6
// let ary3 = [...ary1, ...ary2];
// console.log(ary3)
// 合并数组的第二种方法
// let ary1 = [1, 2, 3];
// let ary2 = [4, 5, 6];
// ary1.push(...ary2);
// console.log(ary1)
// 利用扩展运算符将伪数组转换为真正的数组
var oDivs = document.getElementsByTagName('div');
console.log(oDivs)
var ary = [...oDivs];
ary.push('a');
console.log(ary);
</script>
</body>
9.ES6的内置对象的拓展
1.Array的拓展方法
1.1 Array.from()
<script type="text/javascript">
// var arrayLike = {
// "0": "张三",
// "1": "李四",
// "2": "王五",
// "length": 3
// }
// var ary = Array.from(arrayLike);
// console.log(ary)
var arrayLike = {
"0": "1",
"1": "2",
"length": 2
}
var ary = Array.from(arrayLike, item => item * 2)
console.log(ary)
1.2 find()
var ary = [{
id: 1,
name: '张三'
}, {
id: 2,
name: '李四'
}];
let target = ary.find(item => item.id == 3);
console.log(target)
1.3 findIndex()
let ary = [10, 20, 50];
let index = ary.findIndex(item => item > 15);
console.log(index)
1.4includes()
let ary = ["a", "b", "c"];
let result = ary.includes('a')
console.log(result)
result = ary.includes('e')
console.log(result)
2. String的拓展方法
// let name = `张三`;
// let sayHello = `Hello, 我的名字叫${name}`;
// console.log(sayHello);
// let result = {
// name: "zhangsan",
// age: 20
// };
// let html = `
// <div>
// <span>${result.name}</span>
// <span>${result.age}</span>
// </div>
// `;
// console.log(html);
const fn = () => {
return '我是fn函数'
}
let html = `我是模板字符串 ${fn()}`;
console.log(html)
2.1 starsWith()和endsWith()
let str = 'Hello ECMAScript 2015';
let r1 = str.startsWith('Hello');
console.log(r1);
let r2 = str.endsWith('2016');
console.log(r2)
2.2 replace()
console.log("y".repeat(5))
3. Set 数据结构
// const s1 = new Set();
// console.log(s1.size) // 0
// const s2 = new Set(["a", "b"]);
// console.log(s2.size)
// let arr = ["a","a","b","b"];
// const s3 = new Set(arr)
// console.log(s3.size);
// const ary = [...s3];
// console.log(ary)
// const s3 = new Set(["a","a","b","b"]); //数组去重
// console.log(s3.size)
// const ary = [...s3];
// console.log(ary)
// const s4 = new Set();
// // 向set结构中添加值 使用add方法
// s4.add('a').add('b');
// console.log(s4.size)
// 从set结构中删除值 用到的方法是delete
// const s4 = new Set(["c"])
// const r1 = s4.delete('c');
// console.log(s4.size) // 0
// console.log(r1); // true
// 判断某一个值是否是set数据结构中的成员 使用has
// const r2 = s4.has('d');
// console.log(r2)
// 清空set数据结构中的值 使用clear方法
// s4.clear();
// console.log(s4.size);
// 遍历set数据结构 从中取值
// const s5 = new Set(['a', 'b', 'c']);
// s5.forEach(value => {
// console.log(value)
// })