优雅的JavaScript-面向对象

本文介绍了JavaScript中构造函数创建对象、原型模式、原型链、属性访问器等内容,并详细解析了This关键字的不同应用场景及作用域相关概念。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

构造函数创建对象


<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8">
    <title></title>
    <style type="text/css">


    </style>
</head>
<body>
</body>
</html>
<script type="text/javascript">
//通过构造函数创建对象
    注意:构造函数和普通函数一样,为了区分一般首字母大写
    //声明“类”,用来创建对象
    function Person(name,age) {
        //声明属性,this:代表当前对象实例
        this.name="张三";
        this.age=18;
        //声明一个私有属性,私有属性就是外部函数无法访问到的
        设置方法(访问私有变量)
        this.setHeight=function (height) {

            _height=height;
        }
        //取值方法(访问私有变量)
        this.getHeight=function () {
            return _height;
        }
        声明方法
        this.sayHello=function () {
            alert("大家好,我是"+this.name+",我的年龄:"+this.age);
        }

    }


    //给Person添加一个静态方法(类方法)
,静态方法是直接通过构造函数的名称调用的,不用关心是否有对象实例的存在
    如:Math.random();
    Person.staticFu=function () {
        alert("z这是一个仅仅直接用构造函数名字调用的静态方法")

    }
    //一般我们可以用静态方法区创建对象,可以简化代码,实质上new+构造函数创建对象
    Person.createPerson=function (name,age) {
        var tempPerson=new Person(name,age);
        return tempPerson;


    }
//通过Person构造函数创建一个对象实例
    注意:通过new和构造函数去创建

问题:
     new Person 在内存中做了什么,,通过new 关键字创建的对象,都会存在堆内存中

    只要看到new+构造函数,就代表在堆内存中开辟了一个空间(对象)。同时生产一个地址,如果需要访问这块空间
    只能通过指针变量来访问


    var person1=new Person("张三",9);
    //用对象调用方法
    person1.sayHello();
var person2=person1;
这里没有生产新的对象,而是person2指针指向了和person1指针相同的内存地址
    //修改person2的名字,年龄
    person2.nam="李四";
person2.sayHello();
//结论:
      person1和person2指向的是一个对象

    还可以直接判断两个指针是否指向同一块内存空间
    alert(person1===person2);
在创建一个新的对象
    var person3=new person("sfs",19);

instanceof运算符来判断一个变量是否是通过某个构造函数创建出来

    typeof:判断类型

    //使用静态
    Person.staticFu();

</script>

原型模式创建对象


概念:
  每个构造函数都有一个原型属性(prototype)
  这个原型属性指向一个原型对象,可以给原型对象声明属性和方法,这个属性和方法可以通过构造函数创建出来的对象实体使用。
  或者理解为:
     每个构造函数创建出来的对象实例,继承与这个原型对象的属性和方法
     
,原型模式创建对象其核心还是通过构造函数创建对象,只不过方式是通过原型模式创建


<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8">
    <title></title>
    <style type="text/css">


    </style>
</head>
<body>
</body>
</html>
<script type="text/javascript">
//狗的构造函数
    function Dog(name) {
        this.name=name;
        this.bark=function () {
            alert("小狗叫")
        }
//      全局函数虽然可以解决函数共享问题,但一般不要使用《生命周期太长
this.bark=globalFn;
    }
//创建一个全局函数,这样可以解决每一次创建独享都要开辟一个内存空间去存储function
    function globalFn() {
        alert("dsfd");
    }
    //通过Dog的原型对象声明bark方法,这样也可以达到函数共享
    Dog.prototype.bark=function () {
        alert("原型,vdv");
    }
创建一个狗的对象
    var dog1=new Dog("das");
    var dog2=new Dog("asd");
//    验证:
//    两只狗的bark方法是否指向同一个对象
    console.log(dog1.bark===dog2.bark);
</script>

原型链


每个对象都要一个原型对象,而原型对象也是一个对象,也会有对应的原型对象,以此类推,直到Object.prototype(注意:Object.prototype吃的原型为null),例如:
   a对象是b对象的原型对象,b又是c的原型对象,这样就形成了原型链,c对象可以访问其原型脸上的任何对象里的属性和方法
   
<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8">
    <title></title>
    <style type="text/css">


    </style>
</head>
<body>
</body>
</html>
<script type="text/javascript">
原型相关的属性和方法:

    constructor属性:每个原型对象都有一个constructor属性,这个属性默认是该原型对象所在的构造函数
    obj.hasOwnProperty("属性")方法:用来判断某个对象里是否存在某个属性
    Object.getPrototypeOf(obj)方法:返回一个对象的原型对象
    Object.create(obj):根据接受参数对象,创建一个新的对象,前者是这个新对象的原型对象
    obj.isPrototypeOf(obj2):判断一个对象是否是另外一个对象的原型对象



    function Cat() {

    }
//动物的构造函数
    function Animal() {
        this.name="动物";
    }
    //给Animal构造函数设置原型对象
    //{}:表示字面量创建对象,其内部的语法复合JSON语法格式
    Animal.prototype={
    sex:"男",
        sayHello:function () {
            alert("大家好");
        }


    }
    //给Cat构造函数设置原型对象
    Cat.prototype=new Animal();
//创建一个猫的对象
    var cat1=new Cat();
    //cat1对象先去找自身的构造函数是否有name.age.sex等属性和方法,
    如果有:直接使用自己的
    如果没有:就沿着原型链往上找,找到就使用,如果找不到,直到找到最根部Object.prototype为止
</script>

属性访问器


<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8">
    <title></title>
    <style type="text/css">


    </style>
</head>
<body>
</body>
</html>
<script type="text/javascript">
//人的构造函数
    function Person() {
        //私有属性
        var name="123";
        //给当前对象的name属性添加set和get访问器
        Object.defineProperty(this,"name")
        set:function (newName) {
            if(newName="小明"){
                name=newName;
                return;
            }
        };
get : function(){
    alert("name")
}
    }
</script>

This关键字


在js中所有的函数都运行在某个运行环境中,对于js而言,一切皆是对象,运行环境也是对象,函数中的this关键字的指向,也是根据当前对象(运行环境)而定

在js中,因为js是动态脚本语言,运行环境也是动态切换,也就是说:this关键字的指向也是动态切换

<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8">
    <title></title>
    <style type="text/css">


    </style>
</head>
<body>

<input type="button" value="这是第一个按钮" onclick="btnClick(this)"/>
<input type="button" value="这是第而个按钮" onclick="btnClick(this)"/>
</body>
</html>
<script type="text/javascript">
//在构造函数在,this指向的就是该构造函数所创建的实例对象
    function Person() {
        this.name="asc";
    }


    var Person1=new Person();
    Person1.name="ads";
    //this在这种运行环境中,代表当前点击的按钮对象
    function btnClick() {
        alert("vdsgfh");
    }
运行环境动态切换,this指向也跟着动态切换
    function fn() {
        console.log(this.name)
    }

创建一个字面量对象
    var a={
        name:"a",
        aFn:fn

    }
a.aFn();


</script>



使用场景:
  <!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8">
    <title></title>
    <style type="text/css">


    </style>
</head>
<body>

<input type="button" value="这是第一个按钮" onclick="btnClick(this)"/>
<input type="button" value="这是第而个按钮" onclick="btnClick(this)"/>
</body>
</html>
<script type="text/javascript">
//使用场景
    1.全局运行环境
    console.log(this===window);//true
    2.构造函数中
    function Person() {
        this.name="张三";
    }
    Person.prototype.say=function () {
        alert(this.name);
    }
var person=new Person();
    person.say();


    3.在对象方法中
    var a={
        name:"a",aFn:function () {
            console.log(this.name);
        }
    }

    var b={
        name:"b",bFn:a.aFn

    }
b.bFn();

    4.常见的问题:
      如果某个对象的层次结构很深,我们想把某个方法提取出来,不能直接提取方法,而是提取方法所在的对象
    var c={
        d:{
            aa:"liu",
            fn1:function () {
                console.log(this.aa);
                console.log(this===window);
            }
        }
    }
//提取fn函数
    var myFn=c.d.fn1;//这样提取的话,函数内部的this指向变成window对象
    myFn();//undefinded

如果想把fn函数提取出来,而且不改变this指针,那么就提取fn函数所在的对象
    var maobj=c.d;
maobj.fn1();//liu


    总结:
       1.全局环境中:this指向的是最顶端window对象
      2.构造函数中:this指向的是该构造函数所创建的实例对象
      3.对象方法中:如果某个对象的方法赋值给了另外一个对象的某个方法,
那么方法内部的this指向也会发生变化

</script>

作用域


定义:变量和函数访问的范围,作用域可以控制变量和函数的可见性和生命周期
在js中作用域大概分两种:
   1.全局作用域
   2.局部作用域
   
   <!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8">
    <title></title>
    <style type="text/css">


    </style>
</head>
<body>

<input type="button" value="这是第一个按钮" onclick="btnClick(this)"/>
<input type="button" value="这是第而个按钮" onclick="btnClick(this)"/>
</body>
</html>
<script type="text/javascript">
//全局作用域
//这是一个全局变量,已经处于全局作用域中,也就是说在任何位置都可以访问到这个全局变量
    var globalName="这是全局变量";

    //在全局作用域中访问
    console.log(globalName);

//在普通的函数中访问
    function fn() {
        console.log(globalName);
    }
fn();
    //在构造函数中访问
    function Person() {
        this.name=globalName;
    }
var person=new Person();
    console.log(person.name);

    //在字面量对象中访问
    var obj={
        name:globalName
    }
console.log(obj.name);

    /*局部作用域*/
function fn2() {
    var partName="这是局部变量";
    //局部变量在当前作用域一定可以被访问
    alert(partName);
    function sunfn() {
        alert("某个函数的子函数(内部函数)也可以访问到当前函数的局部变量"+partName);
    }
    sunfn();
}
//会出现异常,try...catch语句:用来捕捉异常(影响成像运行的一些错误)
如果用try...catch来捕捉异常,当异常出现,程序照样会执行
    一般觉得有风险的地方就使用try...catch
    try{
    //把觉得有风险的代码写在try代码块里面、
        console.log(partName);
    }catch(exception){
    //捕捉异常后打印异常信息
        console.log(exception.toString());
    }

    注意事项:
    //1.在js中是没有块级的概念
    function fn() {
        for(var i=0;i<5;i++){

        }
    }
//可以访问到i
    console.log(i);

    //2.不用var声明的变量是全局变量
    function fn2() {
        name="asd";
    }
fn2();
    //可以访问到不用var声明的变量
    console.log(name);

    练习:
       var a="a";
    function fn3(d) {
        b="b";
        var c="c";
        function subFn() {
            var e=d;
            if(true){
                var f="f";
                console.log(e);
            }
            console.log(f);
        }
        sunfn();
    }
    fn3("d");
    console.log(b);

    注意:
       1.在js中,没有块级(大括号)的概念,在其他语言都有块级概念,
在块级变量中的变量就是局部变量,但在js中的块级中的变量不是局部变量,
在js中,只有在函数中的才是局部变量
      2.在js中,如果没有用var声明变量的话,那么这个变量就是全局变量,在任何位置都可以被访问


</script>

作用域链


定义:作用域可以理解和原型链意思差不多
也就是访问某个变量,先去当前的作用域去查找这个变量,如果找不到,就沿着作用域链向上找,以此类推,直到最顶端的window对象,全局作用域


<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8">
    <title></title>
    <style type="text/css">


    </style>
</head>
<body>

<input type="button" value="这是第一个按钮" onclick="btnClick(this)"/>
<input type="button" value="这是第而个按钮" onclick="btnClick(this)"/>
</body>
</html>
<script type="text/javascript">
//作用域链
    var name="aaa";
    function fn() {
        var name="abc";
        function sunfn1() {
            var name="bcd";
            console.log(name);
        }
        function sunfn2() {
            console.log(name);
        }
        subfn1();
        //其原理:当调用subfn1函数时候,
        会把subfn1所在的执行环境放到作用域链的最底端,
        然后将fn()函数执行环境放到他的上面,最后放到全局的执行环境
//subfn1----->fn---->window
            //subfn2();
        /subfn2----->fn---->window
    }
</script>

固定This的方法


js中this关键字的指向是动态的,动态有好处也有坏处,有时候会出现意想不到的结果,此时需要通过一些方法来固定this,call()/apply()
1.call方法:可以指定函数内部的this指向,并且运行该函数
2.apply方法:和call方法功能一样,只不过参数为数组

<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8">
    <title></title>
    <style type="text/css">
    </style>
</head>
<body>

<input type="button" value="这是第一个按钮" onclick="btnClick(this)"/>
<input type="button" value="这是第而个按钮" onclick="btnClick(this)"/>
</body>
</html>
<script type="text/javascript">
1.call方法

    var name="abc";
var obj={
    name:"bcd"
}
    function fn() {
        console.log(this.name);
    }
    fn();

//通过函数调用call方法,可以改变this 的指向,并且在参数所在的运行环境中运行
    fn.call(obj);
    //带参数
    function fn2(a,b) {
        console.log(a+b);
    }
//在全局环境中运行
    fn2.call(this,5,8);
    //apply方法
    fn2.apply(this,[6,9]);



</script>


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值