创建默认对象
- JavaScript中提供了一个默认的类Object,我们可以通过这个类创建对象
- 由于是用系统默认的类创建的对象,系统不知道我们需要给对象添加什么属性和方式,所以需要手动的添加我们需要的属性和方法
- 如何给一个对象添加属性
对象名.属性名 = 值 ; - 如何给一个对象添加方法
对象名.方法名 = function (){
代码;
}
- 创建对象的方法
第一种方法
let obj = Object(); //创建一个名为obj的对象
obj.name = "wj";//给obj对象添加一个name的属性
obj.say = function(){
console.log("我是对象");
}
obj.say();// 结果 我是对象
第二种方法
let obj = {}; //创建一个名为obj的对象
obj.name = "wj";//给obj对象添加一个name的属性
obj.say = function(){
console.log("我是对象");
}
obj.say();// 结果 我是对象
第三种方法
注意: 属性名称和取值之间用冒号隔开, 属性和属性之间用逗号隔开
let obj = {; //创建一个名为obj的对象
name : “wj” ,//给obj对象添加一个name的属性
say : function(){
console.log("我是对象");
}
}
obj.say();// 结果 我是对象
函数和方法的区别
函数: 没有和其他类绑定在一起的
方法: 和其他类绑定在一起的
函数可以直接调用,方法只能通过对象调用
- 函数和方法中都有一个叫 this 的东西
谁调用了当前的方法或者函数,this就是谁
let obj = { //创建一个名为obj的对象
name : "wj" ,
say : function(){
console.log(this);
}
}
obj.say(); //结果为 obj对象
工厂函数
专门用于创建对象的函数被称为工厂函数.
function createPerson(myName,myAge) { //创建工厂函数
let obj = new Object();
obj.name = myName;
obj.age = myAge;
obj.say = function(){
console.log("我是工厂函数")
}
return obj; //返回对象
}
let obj = createPerson("wj",18);
let obj2 = createPerson("李四",22);
console.log(obj);
console.log(obj2);
构造函数
- 构造函数也是用来创建对象的
构造函数本质是工厂函数的简写 - 构造函数和工厂函数的区别
构造函数的函数名称首字母必须大写
构造函数只能通过new来调用
function Person (myName,myAge) {
//系统会自动添加 let obj = new Object(); let this = obj;
this.name = myName;
this.age = myAge;
this. say = function (){
console.log("我是构造函数");
}
//系统自动添加 return this;
}
let obj = new Person("文件",18);
let obj1 = new Person("wj",21);
console.log(obj);
console.log(obj1);
- 构造函数的优化
每个构造函数中都有一个叫prototype的东西
为了解决性能问题,我们可以将被创建的对象共有的属性和方法放到prototype中
function Person (myName,myAge) {
this.name = myName;
this.age = myAge;
}
Person.prototype = {
this. say = function (){
console.log("我是构造函数");
}
}
let obj = new Person("文件",18);
let obj1 = new Person("wj",21);
console.log(obj.say===obj1.say);//结果为 true
obj.say(); //结果为 我是构造函数
prototype的特点
- 存储在prototype中的方法和属性可以被对应构造函数创建的对象共享
2.如果prototype中的存储的方法或者属性和构造函数的同名,那么访问到的是构造函数中的方法或者属性
prototype的应用场景:主要用来存放所有对象都相同的一些属性以及方法
如果是对象特有的属性或者方法,一般放到构造函数中
函数对象关系
- Function函数狮所有函数的祖先函数,所有对象都是函数
- 所有构造函数都有一个prototype属性,并指向自己的原型对象
- 所有原型对象都有一个constructor属性,constructor指向自己的构造函数
- 所有函数都有proto属性
普通对象的proto属性指向创建它的构造函数的原型对象
所有对象的proto属性最终都会指向"Object原型对象"
Object原型对象proto属性指向"null"
原型.png
原型链
对象中由proto组成的链条,我们称为原型链
对象在查找属性和方法时,会优先会在自身找,如果没有就会去上一级的原型对象中查找,如果找到Object的原型对象中还没有,就会报错。
注意:在给prototype赋值的时候,为了不破坏对象中原有的关系,我们需要手动指定constructor指向谁
function Person (myName,myAge) {
this.name = myName;
this.age = myAge;
}
Person.prototype = {
constructor:Person,
say :function () {
console.log("我是原型对象");
}
}
let obj = new Person("wj",21);
console.log(obj.__proto__ === Person.prototype);//结果为 true
##属性注意点
在JavaScript对象中,如果要给一个对象不存在的属性或者方法设定的值得时候,他不会去原型对象中去查找,而是会给这个对象添加这个不存在的方法或者属性
```javaScript
function Person (myName,myAge) {
this.name = myName;
this.age = myAge;
}
Person.prototype = {
constructor:Person,
sex: 2,
say :function () {
console.log("我是原型对象");
}
}
let obj = new Person("wj",21);
console.log(obj.sex);//结果 2
console.log(obj.__proto__.sex);//结果 2
obj.sex=1;
console.log(obj.sex);//结果 1
console.log(obj.__proto__.sex);//结果 2
三大特性之封装性
- 什么是封装? 就是隐藏实现的细节,仅对外公开接口
- 为什么要封装?
- 当你的属性暴露在外部时,封装可以防止别人随意的修改你的数据
- 封装是将数据隐藏起来,只有用特定的方法才可以设置或者调用数据,不能被外界随意修改,这样降低了数据被误用的可能
function Person (myName,myAge) {
this.name = myName;
let age = 45;
this.setAge = function (myAge) {
if(myAge>0&&myAge<150)
age = myAge;
}
this.getAge = function () {
return age;
}
}
Person.prototype = {
constructor:Person,
say :function () {
console.log("我是构造函数");
}
}
let obj = new Person("wj",21);
console.log(obj.getAge()); // 结果 21
obj.setAge(86);
console.log(obj.getAge());// 结果 86
-
私有属性注意点:
在给一个对象不存在的属性设置值的时候, 不会去原型对象中查找, 如果当前对象没有就会给当前对象新增一个不存在的属性
由于私有属性的本质就是一个局部变量, 并不是真正的属性, 所以如果通过 对象.xxx的方式是找不到私有属性的, 所以会给当前对象新增一个不存在的属性
function Person (myName,myAge) {
this.name = myName;
let age = 45;
this.setAge = function (myAge) {
if(myAge>0&&myAge<150)
age = myAge;
}
this.getAge = function () {
return age;
}
}
Person.prototype = {
constructor:Person,
say :function () {
console.log("我是构造函数");
}
}
let obj = new Person("wj",21);
console.log(obj.getAge()); // 结果 21
obj.setAge(86);
console.log(obj.getAge());// 结果 86
obj.age= -3; //当前对象新增一个不存在的属性age
console.log(obj.age); //结果为 -3
属性方法分类
通过实例对象调用的方法或者属性,被称为实例方法或者属性
通过构造函数调用的方法或属性,被称为静态方法或者属性
三大特性之继承
在企业开发中如果构造函数和构造函数之间的关系是is a关系, 那么就可以使用继承来优化代码, 来减少代码的冗余度
-
继承第一代
在子类构造函数之后手动设置子类构造函数的prototype属性指向父类的实例对象,为了维持他们之间的关系,也需要设定父类实例对象的constructor指向子类构造函数————
- 子构造函数名.prototype = 父类实例对象
- 父类实例对象.constructor = 子类构造函数名
function Person (myName,myAge) {
this.name = myName;
this.age = myAge;
}
Person.prototype = {
constructor:Person,
say :function () {
console.log("我是原型对象");
}
}
let obj = new Person("wj",21);
function Student (myName,myAge,myScore) {
this.score = myScore;
this.say = function() {
console.log("我是子类");
}
Student.prototype = new Person();
Student.prototype.constructor = Student;
let stu = new Student("w",12,88); //在Student构造函数中找不到myName,myAge对应的属性
所以该方法有个弊端 不能在创建Student对象的同时给继承父类属性赋值
-
继承第二代
this是什么? this是谁调用了函数或者方法,this就指向谁 - bind方法
通过bind方法可以改变this的指向 并会新生成一个函数并返回给我们
格式:需要改变this指向的函数名.bind(新指向的对象)
function test(){
name : 12;
}
function fn(){
console.log(this);
}
fn();//返回window
let obj = fn.bind(test);
obj(); //返回 test
bind方法改变this指向的同时也可以设定参数,只要将设定的参数写在新指向的对象名后面,用逗号隔开
function test(){
name : 12;
}
function fn(a,b){
console.log(a,b);
console.log(this);
}
fn(1,10);//返回1 10 window
let obj = fn.bind(test,2,9);
obj(); //返回 2 , 9 test
- call方法
通过call方法改变this的指向 会立即调用修改之后的函数
格式:需要改变this指向的函数名.call(新指向的对象)
function test(){
name : 12;
}
function fn(){
console.log(this);
}
fn();//返回window
fn.call(test);//返回 test
call方法改变this指向的同时也可以设定参数,只要将设定的参数写在新指向的对象名后面,用逗号隔开
function test(){
name : 12;
}
function fn(a,b){
console.log(a,b);
console.log(this);
}
fn(1,10);//返回1 10 window
fn.call(test,2,9); //返回 2 , 9 test
- apply方法
通过apply方法改变this的指向 会立即调用修改之后的函数
格式:需要改变this指向的函数名.apply(新指向的对象)
function test(){
name : 12;
}
function fn(){
console.log(this);
}
fn();//返回window
fn.apply(test);//返回 test
call方法改变apply指向的同时也可以设定参数,只不过需要用数组来传递参数
function test(){
name : 12;
}
function fn(a,b){
console.log(a,b);
console.log(this);
}
fn(1,10);//返回1 10 window
fn.apply(test,[2,9]); //返回 2 , 9 test
继承方法二 用call方法在子类构建函数中改变父类的this指向,这种方法解决了不能在创建Student对象的同时给继承父类属性赋值
//建议去温习一下工厂函数和构造函数
function Person (myName,myAge) {
//let obj = new Object();
//let this = obj;
// this = stu ;
this.name = myName;
this.age = myAge;
// return this;
}
let obj = new Person("wj",21);
function Student (myName,myAge,myScore) {
//let stu = new Object();
//let this = stu;
Person.call(this,myName,myAge); //Person.call(stu,myName,myAge);
this.score = myScore;
this.say = function() {
console.log("我是子类");
}
}
let stu = new Student("ww",21,99);
console.log(stu);
弊端:父类构造函数的原型对象中的东西无法获取
-
继承第三代
改变子类构造函数的prototype属性指向父类的原型对象,解决了不能获取父类原型对象中的数据的弊端
function Person (myName,myAge) {
this.name = myName;
this.age = myAge;
}
Person.prototype.speak= function () {
console.log("我是父类的");
}
function Student (myName,myAge,myScore) {
Person.call(this,myName,myAge);
this.score = myScore;
this.say = function() {
console.log("我是子类");
}
}
Student.prototype = Person.prototype ;
Student.prototype.constructor = Student;
let stu = new Student("ww",21,99);
console.log(stu);
stu.speak();//结果 我是父类的
弊端:破坏了原有对象的三角恋关系,由于父类和子类的原型对象是同一个,如果给子类添加方法或者属性,父类也会同步添加这个方法或者属性
-
继承第四代(终极方案)
方法:
- 在子类的构造函数中通过call借助父类的构造函数
- 将子类的原型对象修改为父类的实例对象
该方式解决了继承第三代的弊端
function Person (myName,myAge) {
this.name = myName;
this.age = myAge;
}
Person.prototype.speak= function () {
console.log("我是父类的");
}
function Student (myName,myAge,myScore) {
Person.call(this,myName,myAge);
this.score = myScore;
this.say = function() {
console.log("我是子类");
}
}
Student.prototype = new Person() ;
Student.prototype.constructor = Student;
let stu = new Student("ww",21,99);
Student.prototype.run = function(){
console.log("run");
}
let per = new Person();
per.run(); //报错 解决了继承第三代的弊端
三大特性之多态
1.什么是强类型语言, 什么是是弱类型语言
1.1什么是强类型语言:
一般编译型语言都是强类型语言,
强类型语言,要求变量的使用要严格符合定义
例如定义 int num; 那么num中将来就只能够存储整型数据
1.2什么是弱类型语言:
一般解释型语言都是弱类型语言,
弱类型语言, 不会要求变量的使用要严格符合定义
例如定义 let num; num中既可以存储整型, 也可以存储布尔类型等
1.3由于js语言是弱类型的语言, 所以我们不用关注多态
2.什么是多态?
多态是指事物的多种状态
例如:
按下 F1 键这个动作,
如果当前在 webstorm 界面下弹出的就是 webstorm 的帮助文档;
如果当前在 Word 下弹出的就是 Word 帮助;
同一个事件发生在不同的对象上会产生不同的结果。
3.多态在编程语言中的体现
父类型变量保存子类型对象, 父类型变量当前保存的对象不同, 产生的结果也不同
ES6类和对象
从ES6开始系统提供了一个名称叫做class的关键字, 这个关键字就是专门用于定义类的
格式 :
class 类名 {
实例属性;
实例方法;
静态属性;
静态方法;
}
如果想在创建对象的时候动态的设置属性或者方法,可以在constructor构造函数中设定
class Person {
//当创建对象的时候系统会自动调用constructor构造函数
constructor(myAge) {
this.age = myAge;
}
name = "wj"; //实例属性
say(){
console.log("我是实例方法");
}
static sex = 2 ; //静态属性
static test(){
console.log("我是静态方法");//静态方法
}
}
let per = new Person(21);
console.log(per);
但是以上实例属性和静态属性的写法并不是ES6的标准写法,因此大部分的浏览器都不支持
标准写法是将实例属性写到constructor()里面。
在ES6的标准中,static只能用来定义静态方法,不能定义静态属性,静态属性只能动态的添加
class Person {
//当创建对象的时候系统会自动调用constructor构造函数
constructor(myAge) {// constructor中的东西相当于ES6之前构造函数中的内容
this.age = myAge;
this.name = "wj"; //实例属性
this.say = function() {
console.log("我是实例方法");
}
}
speak(){ //在constructor中的定义的方法会直接存放到原型对象中
console.log("我在原型对象中");
}
static test(){
console.log("我是静态方法");//静态方法
}
}
Person.sex = 2 ; //静态属性(只能通过这种方法定义静态属性)
let per = new Person(21);
console.log(per);
如果通过class定义类, 那么不能自定义这个类的原型对象
如果想将属性和方法保存到原型中, 只能动态给原型对象添加属性和方法
class Person {
constructor(myAge) {
this.age = myAge;
this.name = "wj"; //实例属性
this.say = function(){
console.log("我是实例方法");
}
}
static test(){
console.log("我是静态方法");//静态方法
}
}
Person.sex = 2 ;//定义静态变量
/*Person.prototype.speak = function() {
console.log("我是动态添加的");
}
let per = new Person(21);
console.log(per);
per.speak(); // 结果 我是动态添加的
*/
let obj ={//定义这个类的原型对象
constructor: Person ,
name : 12,
speak:function() {
console.log("我是定义的原型对象");
}
}
Person.prototype = obj ;
let per = new Person(21);
console.log(per);
per.speak(); //报错
ES6继承
ES6中有专门的继承方法:通过extends继承
继承格式:class 子类名 extends 父类名 {}
子类在constructor函数中可以通过super调用父类的构造函数
class Person {
constructor(myAge){
this.age = myAge;
this.say = function () {
console.log("我是父类");
}
}
speak(){
console.log(this.age);
}
}
class Student extends Person {//继承
constructor(myAge,myScore){
super(myAge);//调用父类的构造函数(相当于Person.call(Student,myAge,myScore))
this.score = myScore;
}
study(){
console.log("我是学生");
}
}
let stu = new Student(21,99);
stu.speak();//结果 21
获取对象类型
如果想知道某个对象是由谁创建的可以通过console.log(对象名.constructor.name);来获取
class Person {
constructor(myAge){
this.age = myAge;
this.say = function () {
console.log("我是父类");
}
}
}
let obj = new Person();
console.log(obj.constructor.name); //结果为 Person
instanceof关键字
instanceof关键字用于判断对象是否是谁的实例 如果是返回true 否则返回false
class Person {
constructor(myAge){
this.age = myAge;
this.say = function () {
console.log("我是父类");
}
}
}
let obj = new Person();
console.log(obj instanceof Person); //结果为 true
注意 如果判断的构造函数的原型在实例对象的原型链上 也会返回true
class Person {
constructor(myAge){
this.age = myAge;
this.say = function () {
console.log("我是父类");
}
}
}
class Student extends Person{
constructor(myAge){
super(myAge);
}
}
let obj = new Student(12);
console.log(obj instanceof Student); //结果为 true
console.log(obj instanceof Person); //结果为 true
isPrototypeOf
isPrototypeOf ()用于判断一个对象是否是另一个对象的原型
class Person {
constructor(myAge){
this.age = myAge;
this.say = function () {
console.log("我是父类");
}
}
}
let obj = new Person();
console.log(Person.prototype.isPrototypeOf(obj)); //结果为 true
注意 只要调用者在传入对象的原型链上就会返回true
class Person {
constructor(myAge){
this.age = myAge;
this.say = function () {
console.log("我是父类");
}
}
}
class Student extends Person{
constructor(myAge){
super(myAge);
}
}
let obj = new Student(12);
console.log(Person.prototype.isPrototypeOf(obj)); //结果为 true
console.log(Student.prototype.isPrototypeOf(obj)); //结果为 true
判断对象属性是否存在
格式: "属性名" in 对象名 :
如果在对象的类里或者原型对象里有就返回true,否则返回false
class Person {
constructor(){
this.name = "wj";
}
}
Person.prototype.age = 18;
let obj = new Person();
console.log("name" in obj);//true
console.log("age" in obj);// true
格式 对象名.hasOwnProperty("属性名");
该方法值会在类本身查找,不会去原型对象中查找
class Person {
constructor(){
this.name = "wj";
}
}
Person.prototype.age = 18;
let obj = new Person();
console.log(obj.hasOwnProperty("name"));//true
console.log(obj.hasOwnProperty("age"));// false
对象的增删改查
增添改操作
有两种格式:
- 对象名.属性/方法名 = 值;
- 对象名["属性/方法名"] = 值;
删除操作
两种格式: - delete 对象名.属性/方法名;
- delete 对象名["属性/方法名"];
class Person{
}
let obj = new Person();
obj["name"] = "wj"; //添加属性
obj["say"] = function (){ //添加方法
console.log("我是添加的")
}
delete obj["name"]; //删除属性
对象遍历
通过高级for循环可以拿到对象中所有的属性和方法名称
格式 for(let key in 对象名){//会将对象中所有的方法和属性取出来依次赋值给key
}
```javaScript
class Person {
constructor(myAge,myName){
this.age = myAge;
this.name = myName;
this.say = function () {
console.log("我是父类");
}
}
speak(){ // 该方法存放在原型对象中
console.log("我在原型对象中");
}
}
let obj = new Person(21,"wj");
for(let key in obj) {
//console.log(obj.key); //结果为undefined(p.key意思是obj中名为
key的属性,obj中并没有,所以结果为未定义)
console.log(obj[key]);//相当于obj["key"] = obj["age"] 取出的才是属性和方法
}//结果中没有speak方法是因为高级for循环不会载原型对象中查找
//如果不想取出对象中的方法可以通过如下方法来实现
class Person {
constructor(myAge,myName){
this.age = myAge;
this.name = myName;
this.say = function () {
console.log("我是父类");
}
}
speak(){ // 该方法存放在原型对象中
console.log("我在原型对象中");
}
}
let obj = new Person(21,"wj");
for(let key in obj) {
if(obj[key] instanceof Function){
continue;
}
console.log(obj[key]);
}
对象解构
对象解构和数组一样
不同在于:
1.符号不同,数组用[], 对象用{};
- 对象解构中左边的变量名称必须和右边的一致,否则解构不出来
class Person {
constructor(myAge,myName){
this.age = myAge;
this.name = myName;
this.say = function () {
console.log("我是父类");
}
}
}
let obj = new Person(21,"wj");
let {name,age} = obj ;
console.log(age,name);// 21 "wj"
应用场景 数组或者对象赋值的时候简化代码
class Person {
constructor(myAge,myName){
this.age = myAge;
this.name = myName;
this.say = function () {
console.log("我是父类");
}
}
}
let obj = new Person(21,"wj");
function speak({name,age}) {
console.log(name,age);
}
speak(obj); // "wj" 21
深拷贝和浅拷贝
深拷贝是指给给新变量赋值时,不会影响原先变量的值
默认情况下基础类型的都是深拷贝
浅拷贝是指给给新变量赋值时,也会改变原先变量的值
默认情况下引用类型的都是浅拷贝
let num1 = 123; //深拷贝
let num2 = num1;
num2 = 666; // 修改形变量的值
console.log(num1); // 123
console.log(num2); //666
class Person{ //浅拷贝
name = "lnj";
age = 34;
}
let p1 = new Person();
let p2 = p1;
p2.name = "zs"; // 修改变量的值
console.log(p1.name);// "zs"
console.log(p2.name); "zs"
对象深拷贝
如果对象中只有基础类型的属性的话可以通过assign方法实现
assign可以将第二个参数的对象的属性拷贝到第一个参数的对象中
class Person {
constructor(myAge, myName) {
this.age = myAge;
this.name = myName;
this.say = function () {
console.log("我是父类");
}
}
}
class Student {
}
let obj = new Person(21,"wj");
let p2 = new Object();
Object.assign(p2,obj);//深拷贝
console.log(p2);
如果对象中含有方法的话,用上面的办法就能完全的深拷贝
这个时候就需要我们自定义一个方法来实现深拷贝
function deepCopy(a,b) {//自定义方法实现对象深拷贝
for(let key in b){// 1.通过遍历拿到source中所有的属性
let bValue = b[key];// 2.取出当前遍历到的属性对应的取值
if(b[key] instanceof Object){// 3.判断当前的取值是否是引用数据类型
a[key] = new bValue.constructor;
deepCopy(a[]key],bValue);//运用递归拷贝对象
}
else {
a[key] = bValue;
}
}
}
网友评论