目录
一、this关键字
每个函数内部都有一个this,函数内部的this和函数的调用方式有关,和定义方式无关!
//1- 全局的this 指向的window window是顶级对象 //2- 每个普通函数中 都有一个this (函数内部的this和函数的调用方式有关,===>谁调用this就执行谁) //3- 在事件处理函数中,this指向触发事件的元素 //4- 全局函数(window) 立即执行函数 匿名函数(window) this 都执行window //5- 箭头函数的this 箭头函数没有自己的this 箭头头函数的this指向箭头函数的上下文的this //6-构造函数 加new 调用 构造函数内部的this 指向 构造函数创建的对象 // - 构造函数内部方法 中的this 执行的是调用方法的对象
修改this的指向
1- 借助that 保存
<!--
改变this的方法
1- 函数外部定义that that的值是想要的this 在函数内部使用that
就可以在函数内使用到外部的this;
-->
<script>
function Dog(name){
this.username = name;
console.log(this);//d1
let that = this;//that ==>d1
this.run =function(){
console.log(this);//d1
setTimeout(function(){
console.log(this);//window
console.log(that.username);//undifined
},1000)
}
}
let d1 = new Dog("小黑");
d1.run();
</script>
2- 使用箭头函数
function Dog(name){
this.username = name;
console.log(this);//d1
this.run =function(){
console.log(this);//d1
setTimeout(()=>{
console.log(this);//window
console.log(this.username);
},1000)
}
}
let d1 = new Dog("小黑");
d1.run();
3- call 、 apply bind 修改this的指向
function fn(a,b){
console.log(this);//
console.log(a,b);
}
// fn(1,2);
// 借助call方法 调用函数 里边写参数 第一个参数可以修改this的指向,,后边的参数都是你调用函数传的实参
let obj = {name:"李白"}
// fn.call(obj,1,2);
// 借助apply 方法 调用函数 里边写参数,第一个参数可以修改this的指向,第二个参数是个数组,数组的内容你调用函数传的实参
// fn.apply(obj,[1,2]);
// 借助bind方法
// 不会直接调用fn函数,返回一个新的函数,新的函数和fn函数是一样的,
// 区别就是在新函数中执行的时候this 是第一个参数传入的对象,如果有实参 在调用新函数时传入
let newFn = fn.bind(obj);
newFn(1,2)
二、原型和原型链
原型 解决了构造函数的一个缺点
// 什么原型?
// 每一个构造函数中都有一个prototype(显式原型)属性,这个就叫原型,上面存了构造函数的方法,
// 每个对象都有一个__proto__(隐式原型) 属性 指向了构造函数的prototype
// 当我们使用对象的属性或者方法时,先在自身查找,找不到就去__proto__找
// 原型链?
// 当我们使用对象的属性或者方法时,先在自身查找,找不到就去__proto__找
// 如果__proto__没有 继续找 因为__proto__也是个对象 所以它也有__proto__属性
// d1.__proto__.__proto__ 以此类推
// 直到找到Object.prototype 就结束了,因为Object.prototype的原型是null
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<script>
// 构造函数创建对象 相当于是个模板
function Dog(name) {
this.name = name;
}
Dog.prototype.run=function(){
console.log(this.name + "会跑");
}
let d1 = new Dog("小黑");
let d2 = new Dog("小白");
// 地址不一样 堆和栈
console.log(d1 === d2);//false
// 栈内存
// d1:001
// d2:002
// 堆内存
// 001 {name:"小黑",run:003}
// 002 {name:"小白",run:004}
// 003 function(){}
// 004 function(){}
// .....
console.log(d1.run === d2.run);//
// js内置的构造函数 怎么样做的??
// 公共的方法 都放到了原型(prototype)里
// 原型里方法 只占用一块内存空间,每次用这个方法时用的都是同一个
let arr1 = new Array(1, 2, 3);
let arr2 = new Array(4, 5, 6);
console.log(arr1 === arr2);//false
console.log(arr1.push === arr2.push);//true arr1 和 arr2 的push方法 存到一个地址里边
// 什么原型?
// 每一个构造函数中都有一个prototype(显式原型)属性,这个就叫原型,上面存了构造函数的方法,
// 每个对象都有一个__proto__(隐式原型) 属性 指向了构造函数的prototype
// 当我们使用对象的属性或者方法时,先在自身查找,找不到就去__proto__找
console.log(Array.prototype.push === arr1.__proto__.push);//true
console.log(Array.prototype.push === arr2.__proto__.push);//true
console.log(Array.prototype === arr1.__proto__);//true
console.log(Array.prototype === arr2.__proto__);//true
console.log(arr1.push);
// 原型链?
// 当我们使用对象的属性或者方法时,先在自身查找,找不到就去__proto__找
// 如果__proto__没有 继续找 因为__proto__也是个对象 所以它也有__proto__属性
// d1.__proto__.__proto__ 以此类推
// 直到找到Object.prototype 就结束了,因为Object.prototype的原型是null
// hasOwnProperty 判断对象中有没有某个属性
console.log(d1.__proto__.__proto__);
console.log(d1.__proto__.__proto__.__proto__);//null
console.log(d1.__proto__.__proto__==Object.prototype);//true
</script>
</body>
</html>
三、创建对象
通过构造函数创建 (es5)
通过类创建 (es6)
通过类创建 是 通过构造函数创建的一个语法糖
// 通过类创建对象 语法糖
// 其实就是对通过构造函数创建的 一种封装
// 定义类 class
// 实例属性和方法 必须创建对象 在创建的对象上使用 new
// 静态属性和方法 可以不创建 直接Person.XXX 使用
class Person{
// 类在使用时 必须加new 一旦new了 就会自动的执行constructor函数
// constructor 里一般放属性
constructor(name){
// let this = {}
this.name = name; //实例属性/对象属性
}
// 直接定义Person类的方法 直接定义到原型上了
study(){ //实例方法/对象方法
console.log(this.name+"会学习");
}
// 静态属性 static
static a = 1;
static test(){
console.log("test");
}
}
// let p1 = new Person("小张");
// let p2 = new Person("小李");
// console.log(p1.study === p2.study);//true
console.log(Person.a);
console.log(Person.test);
四、浅拷贝和深拷贝 ctrl+c
浅拷贝: 只拷贝一层
浅拷贝 拷贝的是地址 只要有一个改了 其他的都会改
列举了三种浅拷贝的写法
// 1- 展开运算符... 实现浅拷贝
let obj2 = {
...obj1
}
obj2.name = "123"
// console.log(obj1,obj2);
// 2- Object.assign() 实现浅拷贝
let obj3 = {}
Object.assign(obj3, obj1);
//obj3.child.name = "123";
//console.log(obj1,obj3);
// 3- 循环遍历实现浅拷贝
function copy(obj) {
let obj4 = {}
// 循环对象
for (let key in obj) {
// console.log(key,obj1[key]);
obj4[key] = obj[key]
}
return obj4;
}
let res = copy(obj1)
console.log(res);
深拷贝: 可以拷贝多层
深拷贝就是完全拷贝一个对象的所有属性值,
如果第二层以后的属性值是一个对象, 不要直接把地址赋值,而是创建一个新的对象!!
// 深拷贝就是完全拷贝一个对象的所有属性值,
// 如果第二层以后的属性值是一个对象, 不要直接把地址赋值,而是创建一个新的对象!!
var obj1 = {
name:"张三",
child:{
name:"张三丰",
child:{
name:"小张三丰",
}
}
}
// 递归 实现深拷贝 (如果对象里由很多属性,写循环跟简单)
// 递归: 函数自己调用自己
function copyDeep(obj){
let obj2 = {}
for(let key in obj){
let value = obj[key];
// 如果value 不是对象 那么value给到obj2
// 如果value 是对象 那么继续定义一个新的对象 接着遍历第二层对象
if(typeof value == "object"){
// 继续调用函数本身
let newObj = copyDeep(value);
obj2[key] = newObj
}else{
obj2[key] = value
}
}
return obj2;
}
let res = copyDeep(obj1);
console.log(res);
console.log(res === obj1);//false
浅拷贝是地址的传递
深拷贝是值的传递
实现深拷贝的方案
1- 借助递归实现
2- 把对象转为json字符串,在利用parse 把字符串解析一个新的属性
let res = JSON.stringify(obj1);
let newObj = JSON.parse(res);
console.log(obj1===newObj);
console.log(obj1.child===newObj.child);
console.log(obj1.child.child===newObj.child.child);
3- 借助第三方库实现 lodash
想要用别人的库 要下载 应用
推荐网站: BootCDN - Bootstrap 中文网开源项目免费 CDN 加速服务 铂特优选 可以从里边下载 你想要的库
lodash官网:Lodash 简介 | Lodash中文文档 | Lodash中文网
let newObj = _.cloneDeep(obj1);