文章目录
- 🌟前言
- 🌟对象:
- 🌟声明对象:
- 🌟隐式创建对象:
- 🌟实例化Object:
- 🌟实例化自定义构造函数:(会重复占用内存)
- 🌟new运算符具体做了什么:
- 🌟instanceof关键字(一元运算符)
- 🌟增
- 🌟删(删除对象或者对象上的属性方法)
- 🌟改(修改属性和方法)
- 🌟查对象(访问)
- 🌟对象的遍历
- 🌟对象的拷贝
- 🌟对象的特性
- 🌟封装
- 🌟封装方法
- 🌟原型:
- 🌟原型链:`本身->构造函数->构造函数的原型->原型的构造函数的原型->->`
- 🌟对象的继承
- 🌟继承方式
- 🌟继承的顺序
- 🌟this
- 🌟this的指向
- 🌟改变this的指向
- 🌟ES6对象扩展
- 🌟属性方法的简洁表示法
- 🌟对象的解构赋值
- 🌟对象的Rest属性
- 🌟写在最后
🌟前言
哈喽小伙伴们,本文将收录在JavaScript【前端童子功】这个专栏里;这个专栏里边会收录一些JavaScript的基础知识和项目实战;希望大家可以多多支持,你们的支持就是我创作的动力;让我们一起来看看吧🤘
🌟对象:
一切皆对象,对象是属性和方法的无序结合。
- 属性:用数据值来描述它的状态。
- 方法:用来改变对象的行为叫做方法,一个方法是一个值为某个函数的大户型。
🌟声明对象:
🌟隐式创建对象:
var cup={
size:"450ml",
color:"red",
material:"glass",
warm:function (){
return "warm";
}
}
console.log(cup.size);
console.log(cup.color);
console.log(cup.material);
console.log(cup.warm());
🌟实例化Object:
var cup=new Object();
cup.size="500ml";
cup.color="blue"
cup.material="glass"
cup.warm=function(){
return "warm"
}
console.log(cup.size);
console.log(cup.color);
console.log(cup.material);
console.log(cup.warm());
🌟实例化自定义构造函数:(会重复占用内存)
构造函数类;实例化对象单个对象
function Cup(size,color,material){
this.size=size;
this.color=color;
this.material=material;
this.warm=function(){
console.log("warm");;
}
this.storeUp=function(water){
return "store up"+water;
}
}
var cup1=new Cup("500ml","red","玻璃")
console.log(cup1);
var cup2=new Cup("450ml","yellow","不锈钢")
console.log(cup2);
var cup3=new Cup("300ml","blue","紫砂")
console.log(cup3);
🌟new运算符具体做了什么:
function Base(){
this.name = "zhangsan";
this.age = 20;
}
var obj = new Base();
// new操作符等价于
var obj = {};
obj.__proto__ = Base.prototype;
Base.call(obj);
- 创建了一个空对象
obj
- 将这个空对象的
__proto__
成员指向了Base函数对象prototype
成员对象。相当于obj拥有了Base原型上的属性方法 - 将Base函数对象的
this
指针替换成obj,然后再调用Base函数,于是就给obj对象赋值了一个name和age成员变量。相当于obj拥有了Base构造函数上的属性方法
var this = Object.create(Peson.prototype);
🌟instanceof关键字(一元运算符)
如果obj对象是构造函数Fun的一个实例,则 obj instanceof Fun 返回 true。
判断一个对象是否是一个构造函数的实例:
obj instanceof Object // true
obj instanceof Object // false
🌟增
对象.属性名=属性值;
对象。方法名=方法‘
🌟删(删除对象或者对象上的属性方法)
-
销毁对象
javascript中的垃圾回收机制在对象没有引用的时候会进行销毁,释放内存
对象=null;
-
删除对象上的属性和方法,使用
delete
运算符- 删除属性:
delete 对象.属性名
- 删除方法:
delete 对象.方法名
var obj={ a:'a', b:function(){console.log('这是一个方法')} } delete obj.a; //删除obj对象上的a属性 delete obj.b; //删除obj对象上的b方法
- 删除属性:
🌟改(修改属性和方法)
直接为属性或方法赋新值即可
对象.属性名=新的属性值;
对象.方法名=新的方法;
🌟查对象(访问)
-
访问属性
对象.属性名; 对象["属性名"]; //字符串
-
访问方法
对象.方法名(); 对象["方法名"](); //字符串
注:当属性名或方法名保存到变量中时,访问该属性或方法需要使用
[]
for(let i in obj){ obj[i] // i是存储属性名的变量,所以访问该属性时必须通过[] }
🌟对象的遍历
-
for in 循环
循环遍历对象
自身
的和继承
的可枚举属性(不含Symbol属性).
var obj={
name:'小米',
age:18,
say:function(){
console.log('你好');
}
}
for (var i in obj) {
console.log(obj[i]);
}
🌟对象的拷贝
由于对象是引用类型,变量中存储的是数据的地址,所以对象直接通过=
赋值只能将地址赋值而不是数据。
对象的拷贝分为浅拷贝、深拷贝:
var obj = {
type: "animal",
cat:{
name:"Tom",
weight: 16,
food:["fish","meat"]
},
}
-
浅拷贝: 直接拷贝对象的内存地址,如果原地址中对象被改变了,那么浅拷贝出来的对象也会相应改变
- 1:直接复制
var newobj=obj;
- 2:
Object.assign(obj)
var newobj=Object.aaign(obj)
- 深拷贝: 新开辟一块内存,将对象中所有属性全部复制,如果原地址中对象被改变了,那么深拷贝出来的对象不变
深拷贝实现方法:
- 递归遍历,逐层拷贝。 因为基础类型可以直接拷贝,所以通过递归遍历对象的每一层,全部得到基础类型后再拷贝。
function deepCopy(0){
var newobj={};
for(var i in o){
if(typeof o[i]=="bject"){
newobj[i]=deepCopy(0[i]);
}else{
newobj[i]=o[i];
}
}
return newobj;
}
var newobj=deepCopy{obj};
- 通过
JSON.stringify()
先将对象转化为字符串,字符串赋值后再通过JSON.parse()
转化回对象。(方法无法转换)
JSON
是轻量级数据交互的格式
- Object.assign({},obj)`
var newobj=Object.assign({},obj)
🌟对象的特性
- 封装
- 继承
🌟封装
封装: 将对象的所有组成部分组合起来,尽可能的隐藏对象的部分细节,使其受到保护,只提供有限的接口与外部发生联系。
例如同时生成多个相同的对象,可以将生成对象的过程打包直接调用,这个过程就是封装
优点:
- 安全,使用时无法看到具体实现细节,只需要直接调用
- 便于修改操作
🌟封装方法
-
工厂函数(不推荐使用)
将创建对象并赋值的过程封装成一个函数
function person(name,sex){ var person = {}; person.name = name; person.sex = sex; person.say = function(){ alert("说话"); } return person; } var zhangsan = person("张三","man"); alert(zhangsan.name);
-
构造函数(每创建一个对象,会把相同的代码存储到内存中,会造成对内存的浪费)
function person(name,sex){ this.name = name; this.sex = sex; this.say = function(){ alert("说话"); } } var lisi = new person("李四","boy"); alert(lisi.sex);
每次使用工厂函数或构造函数,内部的属性和方法都会在内存中重新开辟一个控件存储生成的对象,导致某些相同的方法或属性被重复保存,占用内存。
-
prototype方法(会把共享的方法或属性放到代码段中来存储,它不能共享对象)
构造函数+原型
实例一:
person.prototype.eat=function(){ alert("吃饭"); } var lisi=new person("李四","boy"); lisi.eat();
实例二:
person.prototype.aaa = {name:"王五"}; var lisi = new person("李四","boy"); var zhaoliu = new person("赵六","boy"); alert(lisi.aaa.name = "xiaosi"); //xiaosi alert(zhaoliu.aaa.name); //xiaosi 将原型上的属性值一起改了
-
混合函数 最佳的一种方法,构造函数与prototype的结合,根据实际情况考虑
私有属性方法放到构造函数中,共有方法放到原型中
function person(user, sex){ this.user = user; this.sex = sex; } person.prototype = { eat: funciton(){ return "eat"; } play: funciton(){ return "play"; } }
🌟原型:
通过工厂函数与构造函数的方式实例化对象,每实例化一个对象就会重新开辟一片内存空间,造成空间浪费,把公共的属性和方法放在构造函数的原型上,并不会重新开辟空间,节省内存。
原型是构造函数的属性。构造函数.prototype={}
放到原型上的方法与属性,在对象的_ proto _中找到。
🌟原型链:本身->构造函数->构造函数的原型->原型的构造函数的原型->->
访问对象的属性或方法时遵循的链式规则,该属性或方法会在对象本身调用,对象本身没有则去对象本身的构造函数调用,本身构造函数没有则去父类的构造函数调用、父类的原型…以此类推,直到寻找至Object、以及Object的原型、null。最后属性不存在时会得到
undefined
,方法不存在则会报错。
ject.prototype.say=function(){
alert("Object的方法");
}
function person(){
this.say=function(){
alert("person的方法");
}
}
person.prototype.say=function(){
alert("person原型的方法");
}
function student(){
this.say=function(){
alert("student的方法");
}
}
student.prototype=new person();
var xiaoming=new student();
xiaoming.say=function(){
alert("xiaoMing的方法");
}
xiaoming.say();
🌟对象的继承
继承:一个对象拥有另一个对象的属性与方法
- 父类(基类):被继承的对象,
- 子类:继承的对象。
优点:
提高代码的重用性,提高代码的逻辑性与可维护性。
🌟继承方式
-
原型继承(将父类的实例作为子类的原型)
原理:对象访问属性方法会遵循 "构造函数 -> 原型"的顺序,所以将父类的实例放到子类原型时, 子类实例化出的对象就可以访问到原型上父类的内容,从而实现了继承。
function Animal(){ this.age=age; this.eat=function(food){ alert(food) } } function Cat(){ } Cat.prototype = new Animal(); // 将父类(Animal)的实例作为子类(Cat)的原型 var tom = new Cat(); cat.eat('fish') // 弹出 'fish' console.log(tom instanceof Animal); //true console.log(tom instanceof Cat); //true
-
call
FN.call(obj,参数1,参数2....)
格式:fun.call(obj2,参数1, 参数2…)
本质上来说,call方法实际上就是要改变fun函数内的this指向。function Animal () { this.eat = function(food){ alert(food) } } function Cat () { this.name = "tom"; } var animal = new Animal (); var cat = new Cat (); animal.eat.call(cat, "鱼") // 继承单个方法(改变单个方法的this指向) // 继承整个构造函数 (改变整个构造函数的this指向) Animal.call(cat);//(方法立即执行) cat.say("鱼")
-
apply
用法基本与call相同,函数的参数通过数组传递
格式:fun.apply(obj2,[参数1, 参数2…])function Animal () { this.eat = function(food){ alert(food) } } function Cat () { this.name = "tom"; } var animal = new Animal (); var cat = new Cat (); animal.eat.apply(cat, ["鱼"]) Animal.apply(cat) cat.eat("鱼")
-
bind继承
-
bing
用法与call
类似,也是可以改变函数体内 this 的指向 -
call与apply是使用时立即执行函数,
bind
是返回对应函数,便于稍后调用
function Animal () { this.eat = function(food){ alert(food) } } function Cat () { this.name = "tom"; } var animal = new Animal (); var cat = new Cat (); animal.eat.bind(cat, "鱼")() // bind只是改变了this指向,想要调用该函数还需()
-
-
ES6类继承
通过
extends
关键字实现类与类之间的继承,然后实例化子类,来实现继承。详见下一节
🌟继承的顺序
优先级:对象本身–>构造函数–>原型链
原型链:
当访问对象的属性或方法时,该属性或方法会在对象本身调用,对象本身没有则去对象本身的构造函数调用,本身构造函数没有则去父类的构造函数调用、父类的原型…以此类推,直到寻找至Object、以及Object的原型、null。最后属性不存在时会得到undefined
,方法不存在则会报错。
Object.prototype.say=function(){
alert("Object的方法");
}
function person(){
this.say=function(){
alert("person的方法");
}
}
person.prototype.say=function(){
alert("person原型的方法");
}
function student(){
this.say=function(){
alert("student的方法");
}
}
student.prototype=new person();
var xiaoming=new student();
xiaoming.say=function(){
alert("xiaoMing的方法");
}
xiaoming.say();
🌟this
this是一个很特别的关键字,被自动定义在所有函数的作用域中。
this总是会指向一个对象。或者说,this就是属性或方法‘当前’所在的对象。
🌟this的指向
this的指向在函数定义的时候是确定不了的,只有函数执行的时候才能确定this到底指向谁,实际上this的最终指向的是那个调用它的对象
- 在全局环境和全局函数中this指
window
全局对象。 - 作为对象方法调用,this 指代调用该方法对象。
- 在构造函数中this指向构造函数的
实例
。 - 在事件中,this指向事件源。
- 在call和apply中,this指的是方法中传入的对象,如果apply中没有传对象,this指向window
function Fun(){
run(){
console.log(this) // this指向实例化出的对象,因为是实例化出的对象调用了run方法
setInterval(_=>{
conosle.log(this) // this指向 window,因为是window调用了setInterval方法
})
}
}
🌟改变this的指向
-
call() ;
- 第一个参数是this的指向,第二个参数为一个参数列表,会立即执行
-
apply() ;
- 第一个参数是this的指向,第二个参数为一个参数数组,会立即执行
-
bind()
- 第一个参数是this的指向,第二个参数为一个参数列表,不会立即执行
🌟ES6对象扩展
🌟属性方法的简洁表示法
当属性名为变量名, 属性值为变量的值时,可直接将变量作为对象的属性:
let username = "张三"
let obj = {
username, // 属性名为变量名, 属性值为变量的值
age: 20
}
-
对象方法的简写:
let obj = { run:function(){ alert(1) } } // 简写为: let obj = { run(){ alert(1) } }
这种写法用于函数的返回值,将会非常方便。
function getPoint() {
const x = 1;
const y = 10;
return {x, y};
}
getPoint()
🌟对象的解构赋值
解构不仅可以用于数组,还可以用于对象。
let { foo: baz } = { foo: 'aaa', bar: 'bbb' };
baz // "aaa"
let { first: f, last: l } = { first: 'hello', last: 'world' };
f // 'hello'
l // 'world'
上面代码中,
foo
是匹配的模式,baz
才是变量。真正被赋值的是变量baz
,而不是模式foo
。
如果变量名与属性名一致,可以写成下面这样:
let { foo, bar } = { foo: "aaa", bar: "bbb" };
foo // "aaa"
bar // "bbb"
对象的解构与数组有一个重要的不同。数组的元素是按次序排列的,变量的取值由它的位置决定;而对象的属性没有次序,变量必须与属性同名,才能取到正确的值。
-
用途1:接收函数的参数
// 接收被除数x与除数y function calc({dividend: x, dividend: y}){ return x/y } let divide = calc({dividend:9,dividend:3})
-
用途2:接收函数的返回值
function calc(a,b){ return { add: a+b, subtract: a-b, multiply: a*b, divide: a/b } } // 相对于返回数组来讲,不需要知道返回值参数的顺序 let {add, subtract, multiply, divide} = calc(1,2)
🌟对象的Rest属性
ES6中数组的rest和扩展运算符,用于数组于普通参数之间的转化
Math.max(...[1,2,3,4])
function fun(a,b,...c){ }
现在在ES9中,当对象结构复制时也可以使用rest运算符:
let obj = {a: 1, b: 2, c: 3};
let {a,...x}
// a == 1
// x == {b: 2, c: 3}
或在函数参数中也可以使用:
restParam({
a: 1,
b: 2,
c: 3
});
function restParam({ a, ...x }) {
// a = 1
// x = { b: 2, c: 3 }
}
🌟写在最后
更多JavaScrip知识以及API请大家持续关注,尽请期待。各位小伙伴让我们 let’s be prepared at all times!
✨原创不易,还希望各位大佬支持一下!
👍 点赞,你的认可是我创作的动力!
⭐️ 收藏,你的青睐是我努力的方向!
✏️ 评论,你的意见是我进步的财富!