JS原型对象

2020/5/31 15:44:11 人评论 次浏览 分类:学习教程

一,构造函数,原型对象,实例的关系:

1,构造函数实例:封装的函数,如果通过new操作符来调用的,就是构造函数,如果没有通过new操作符来调用的,就是普通函数

var person1 = new Person('Mike',10); // 此时的person1 称为person的一个实例,而person就是构造函数了

2,函数Person(对象)有个属性prototype(指针)指向原型对象。 Person.prototype(原型对象,实质也是对象),他有个属性constructor(指针) ,又指向 Person函数对象

console.log(Person === Person.prototype.constructor); //true

3,实例对象person1有个属性[prototype](内部属性,chrome和firefix,Safari,中这个属性叫_proto_)指向原型对象。实例对象可以通过这个属性访问原型对象上的属性和方法(Perons.prototype._proto_ == person1._proto_)。

 

复制代码

//举例:
 function Person(name, age) {
    this.name = name;
    this.age = age;
}
//在原型对象中添加属性或者方法
Person.prototype.sex = '男'; 
var person1 = new Person('Mike',10); 
var person2 = new Person('Alice',20);
//只给person2设置性别
person2.sex = '女';
console.log(person1.sex)  // '男'
console.log(person2.sex)  //'女' 
//解释:这里我们没有给person1实例设置sex属性,但是因为[Protoptype]的存在,会访问原型对象中对应的属性;同时我们给person2设置sex属性后输出的是'女',说明只有当实例本身不存在对应的属性或方法时,才会去找原型对象上的对应属性或方法

复制代码

 

4,可以通过实例对象(person1)的constructor(person1.constructor)访问构造函数,但是constructor本质上是原型对象的属性。

二,继承:

1,继承的主要思路是利用原型链,而原型链的原理是:让一个引用类型继承另一个引用类型的属性和方法。即原型对象通过constructor指向构造函数,实例对象通过_ptoto_指向原型对象。

复制代码

function A() {}

//在A的原型上绑定sayA()方法

A.prototype.sayA = function(){

    console.log("from A")

}

function B(){}

//让B的原型对象指向A的一个实例

B.prototype = new A();

//在B的原型上绑定sayB()方法

B.prototype.sayB = function(){

    console.log("from B")

}

//生成一个B的实例

var a1 = new A();

var b1 = new B();

//b1可以调用sayB和sayA

b1.sayB();//'from B'

b1.sayA();//'from A'

复制代码

说明:2-1,最后我们调用了b1的sayB方法,可以执行,为什么?

因为b1有[Prototype]属性可以访问B prototype里面的方法;

2-2,我们调用了b1的sayA方法,可以执行,为什么?

因为b1沿着[Prototype]属性可以访问B prototype,B prototype继续沿着[Prototype]属性访问A prototype,最终在A.prototype上找到了sayA()方法,所以可以执行

3,JavaScript语言的传统方法是通过构造函数,定义并生成新对象,prototype 属性使您有能力向对象添加属性和方法。下面是通过传统的方式创建和使用对象的案例:

复制代码

//Person.js文件
function Person(x,y){ this.x = x; this.y = y; }
Person.prototype.name = 'sky';  //原型对象上的属性 
Person.prototype.toString = function (){ //原型对象上的方法 

  return (this.x + "的年龄是" +this.y+"岁"); 

} 
export {Person}; 

//index.js文件  

import {Person} from './Person'; 

let person = new Person('张三',12);

console.log(person.toString());

复制代码

 

 

4, 代码中,对象p的的name属性为私有变量,使用p.name不能直接访问

5,以上代码ES6 类class写法:

复制代码

//Person.js

class Person{
    // 构造
    constructor(x,y){  //构造方法
        this.x = x;
        this.y = y;
    }   //方法之间不需要逗号分隔,加了会报错
    toString(){  //原型对象上的方法
        return (this.x + "的年龄是" +this.y+"岁");
    }
}

export {Person};

//index.js

import {Person} from './Person';

let person = new Person('张三',12);

console.log(person.toString());

复制代码

constructor方法是类的构造函数是默认方法,通过new命令生成对象实例时,自动调用该方法。一个类必须有constructor方法,如果没有显式定义,一个默认的constructor方法会被添加。所以即使你没有添加构造函数,也是有默认的构造函数的。一般constructor方法默认返回实例对象this。类的方法内部如果含有this,它默认指向类的实例。。(个人理解:

Person.prototype = this = person1

Person.prototype.constructor = Person,

person1.constructor == Person

person1.__proto__ === person1.constructor.prototype)

6,下面 这个类的名字是Expression而不是Expre,Expre只在Class的内部代码可用,指代当前类。

复制代码

const Expression = class Expre{

    static getAge(){

        return '12';

    }

    getClassName(){

        return " ClassName1= " +Expre.name + " ClassName2= " +Expression.name;

    }

};

let exp = new Expression();

console.log(exp.getClassName());//ClassName1= Expre ClassName2= Expre

console.log(Expression.getAge());

复制代码

说明:Expre.name和Expression.name返回的都是Expre,返回的都是当前类名

如果类的内部没用到的话,可以省略Expre,也就是可以写成下面的形式:

复制代码

const MyExpre = class{

   getClassName(){

        return MyExpre.name;

}

}

let myExpre = new MyExpre();

console.log(myExpre.getClassName());//MyExpre

复制代码

6,立即执行class:

复制代码

let person = new class{

    // 构造

    constructor(props) {

        this.props = props;

    }

    getProps(){

        return this.props;

    }

}('构造函数的参数');

console.log(person.getProps());

复制代码

//构造函数的参数类相当于实例的原型,所有在类中定义的方法,都会被实例继承。如果在一个方法前,加上static关键字,就表示该方法不会被实例继承,而是直接通过类来调用,这就称为“静态方法”。

7, 二次说明:

var o1 = {};

var o2 =new Object();

var o3 = new f1();

 

function f1(){};

var f2 = function(){};

var f3 = new Function('str','console.log(str)');

 

console.log(typeof Object); //function

console.log(typeof Function); //function  

 

console.log(typeof f1); //function

console.log(typeof f2); //function

console.log(typeof f3); //function   

 

console.log(typeof o1); //object

console.log(typeof o2); //object

console.log(typeof o3); //object

 

每个对象都有 __proto__ 属性,但只有函数对象才有 prototype 属性

原型对象(Person.prototype)是 构造函数(Person)的一个实例

构造器

var obj = {}

它等同于下面这样:

var obj = new Object()

obj 是构造函数(Object)的一个实例。所以:

obj.constructor === Object

obj.__proto__ === Object.prototype

同理:

复制代码

var b = new Array();
b.constructor === Array;
b.__proto__ === Array.prototype;
var c = new Date();
c.constructor === Date; 
c.__proto__ === Date.prototype; 
var d = new Function(); 
d.constructor === Function; 
d.__proto__ === Function.prototype;

复制代码

试题:

1,因为 Person的构造函数 === Function

所以 Person.__proto__ === Function.prototype

2,Person.prototype 是一个普通对象,我们无需关注它有哪些属性,因为一个普通对象的构造函数 === Object

所以 Person.prototype.__proto__ === Object.prototype

2,Object.prototype 对象也有proto属性,但它比较特殊,为 null 。因为 null 处于原型链的顶端,这个只能记住。

Object.prototype.__proto__ === null

Function.prototype.__proto__ === Object.prototype

所有函数对象的proto都指向Function.prototype,它是一个空函数(Empty function)

所有对象的 __proto__ 都指向其构造器的 prototype

View Code

 

Number.__proto__ === Function.prototype // true Number.constructor == Function //true

1

Math.__proto__ === Object.prototype // true <br>Math.construrctor == Object // true <br>JSON.__proto__ === Object.prototype // true <br>JSON.construrctor == Object //true

// 上面的函数对象自定义的,

函数声明 function Person() {} // 函数表达式 var Perosn = function() {} console.log(Person.__proto__ === Function.prototype) // true

 

Array.prototype 继承了对象的所有方法,当你用Array.hasOwnPrototype()时,JS 会先查一下它的构造函数 (Array) 的原型对象 Array.prototype 有没有有hasOwnPrototype()方法,没查到的话继续查一下 Array.prototype 的原型对象 Array.prototype.__proto__ === Object.prototype有没有这个方法

 

function Person(name) { this.name = name } // 修改原型 Person.prototype.getName = function() {} var p = new Person('jack') console.log(p.__proto__ === Person.prototype) // true console.log(p.__proto__ === p.constructor.prototype) // true

-----------------------------------------

function Person(name) { this.name = name } // 重写原型 Person.prototype = { getName: function() {} } var p = new Person('jack') console.log(p.__proto__ === Person.prototype) // true console.log(p.__proto__ === p.constructor.prototype) // false

这也很好理解,给Person.prototype赋值的是一个对象直接量{getName: function(){}},使用对象直接量方式定义的对象其构造器(constructor)指向的是根构造器Object,Object.prototype是一个空对象{},{}自然与{getName: function(){}}不等。

 

1,对于私有属性,我们是不可以直接通过 Class 实例来引用的,这也是私有属性的本来含义。但是有一种情况除外,在 Class 定义中,我们可以引用 Class 实例的私有属性:

复制代码

class Foo { 
    #privateValue = 42; 
    static getPrivateValue(foo) { 
      return foo.#privateValue; 
    } 
} 
Foo.getPrivateValue(new Foo()); // >> 42

复制代码

1,数据描述,Object.defineProperty(obj, prop, descriptor) <在ie8下只能在DOM对象上使用>。

obj:必需。目标对象 

prop:必需。需定义或修改的属性的名字

descriptor:必需。目标属性所拥有的特性-->{

configurable: false, //是否可以删除目标属性或是否可以再次修改属性的特性(writable, configurable, enumerable)

enumerable:false, //是否可以被枚举(使用for...in或Object.keys())

value:任意类型的值, //prop key对应的值

writable:false //是否可以被重写

}

以上默认值全为false

var obj = {}

Object.defineProperty(obj,"newKey",{ value:"hello", writable:false, enumerable:false, configurable:true }); //删除属性 delete obj.newKey; console.log( obj.newKey ); //undefined

 

2.存取器描述(注意:当使用了getter或setter方法,不允许使用writable和value这两个属性)

当使用存取器描述属性的特性的时候,允许设置以下特性属性:

复制代码

var obj = {};

Object.defineProperty(obj,"newKey",{

    get:function (){} | undefined,

    set:function (value){} | undefined

    configurable: true | false

    enumerable: true | false

});

var obj = {};

var initValue = 'hello';

Object.defineProperty(obj,"newKey",{

    get:function (){

    //当获取值的时候触发的函数

    return initValue;

},

set:function (value){

    //当设置值的时候触发的函数,设置的新值通过参数value拿到

    initValue = value;

}

});

//获取值

console.log( obj.newKey ); //hello

//设置值

obj.newKey = 'change value';

console.log( obj.newKey ); //change value

复制代码

3, 使用立即执行函数来实现模块化,所谓模块化,就是根据需要控制模块内属性与方法的可访问性,即私有或者公开。在代码中,module为一个独立的模块,N为其私有属性,print为其私有方法,return 里的decription为其公有属性,add为其共有方法。

 

4,变量提升,变量声明被提升了,而变量赋值不会被提升。即被定义了但值为undefined

5,柯里化,即Currying,可以是函数变得更加灵活。我们可以一次性传入多个参数调用它;也可以只传入一部分参数来调用它,让它返回一个函数去处理剩下的参数。

View Code

6,apply方法使用数组指定参数,而call方法每个参数单独需要指定:

  • apply(thisArg, [argsArray])
  • call(thisArg, arg1, arg2, …)
  • this.Arg表示指向的对象,arg1表示_proto_的方法传的形参

View Code

 

使用bind方法,可以为函数绑定this值,然后作为一个新的函数返回:

复制代码

var user = {
  greet: "Hello!",
  greetUser: function(username){
    return this.greet+" "+ username;
  }
}

console.log(user.greetUser("aa"))
var fun = user.greetUser.bind(user);
var fun1 = user.greetUser.bind({greet: "qin"})
console.log(fun("bb"))
console.log(fun1("chuan"))
console.log('---------------------------------')
var user2 = {
    greet : 'Hello2'
}
console.log(user.greetUser.call(user2))
console.log(user.greetUser.call(user2, 'dd'))
console.log(user.greetUser.apply(user2, ['ee']))
> Hello! aa
> Hello! bb
> qin chuan
> ---------------------------------
> Hello2 undefined
> Hello2 dd
> Hello2 ee

复制代码

函数声明可以先调用再声明,而函数表达式必须先定义再调用

 

 

tip:

1,常量大小字母;注释只有在写业务逻辑判断才写

2,词法环境:通过function声明的函数和var 定义的变量,都会有个window预处理阶段,提前调用为,对一个函数的引用和undefuned

 

参考简书:https://www.jianshu.com/p/dee9f8b14771

本想把日子过成诗,时而简单,时而精致,不料日子却过成了一首歌,时而不靠谱,时而不着调

相关资讯

    暂无相关的资讯...

共有访客发表了评论 网友评论

验证码: 看不清楚?
    -->