目录
一、this关键字
修改this的指向
二、原型和原型链
三、创建对象
通过构造函数创建 (es5)
通过类创建 (es6)
四、浅拷贝和深拷贝 ctrl+c
浅拷贝: 只拷贝一层
深拷贝: 可以拷贝多层
一、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);//d1let that = this;//that ==>d1this.run =function(){console.log(this);//d1setTimeout(function(){console.log(this);//windowconsole.log(that.username);//undifined},1000)}}let d1 = new Dog("小黑");d1.run();</script>
2- 使用箭头函数
function Dog(name){this.username = name;console.log(this);//d1this.run =function(){console.log(this);//d1setTimeout(()=>{console.log(this);//windowconsole.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);//falseconsole.log(arr1.push === arr2.push);//true arr1 和 arr2 的push方法 存到一个地址里边// 什么原型?// 每一个构造函数中都有一个prototype(显式原型)属性,这个就叫原型,上面存了构造函数的方法,// 每个对象都有一个__proto__(隐式原型) 属性 指向了构造函数的prototype// 当我们使用对象的属性或者方法时,先在自身查找,找不到就去__proto__找console.log(Array.prototype.push === arr1.__proto__.push);//trueconsole.log(Array.prototype.push === arr2.__proto__.push);//trueconsole.log(Array.prototype === arr1.__proto__);//trueconsole.log(Array.prototype === arr2.__proto__);//trueconsole.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__);//nullconsole.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+"会学习");}// 静态属性 staticstatic 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);