6.22 js学习笔记

本文深入解析JavaScript中的作用域概念,包括局部与全局作用域的区别,变量生命周期,以及函数参数的局部特性。同时,详细阐述闭包的机制,如何利用闭包保护私有变量,并通过实例展示闭包在JavaScript编程中的应用。

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

作用域

JavaScript 作用域

在 JavaScript 中, 对象和函数同样也是变量。

在 JavaScript 中, 作用域为可访问变量,对象,函数的集合。

JavaScript 函数作用域: 作用域在函数内修改。

JavaScript 局部作用域

变量在函数内声明,变量为局部作用域。

局部变量:只能在函数内部访问。

// 此处不能调用 carName 变量
function myFunction() {
    var carName = "Volvo";
    // 函数内可调用 carName 变量
}

因为局部变量只作用于函数内,所以不同的函数可以使用相同名称的变量。

局部变量在函数开始执行时创建,函数执行完后局部变量会自动销毁。

JavaScript 全局变量

变量在函数外定义,即为全局变量。

全局变量有 全局作用域: 网页中所有脚本和函数均可使用。

var carName = " Volvo";
 
// 此处可调用 carName 变量
function myFunction() {
    // 函数内可调用 carName 变量
}

如果变量在函数内没有声明(没有使用 var 关键字),该变量为全局变量。

以下实例中 carName 在函数内,但是为全局变量。

// 此处可调用 carName 变量
 
function myFunction() {
    carName = "Volvo";
    // 此处可调用 carName 变量
}

JavaScript 变量生命周期

JavaScript 变量生命周期在它声明时初始化。

局部变量在函数执行完毕后销毁。

全局变量在页面关闭后销毁。

函数参数
函数参数只在函数内起作用,是局部变量。

HTML 中的全局变量
在 HTML 中, 全局变量是 window 对象: 所有数据变量都属于 window 对象。

//此处可使用 window.carName
 
function myFunction() {
    carName = "Volvo";
}

小结:

局部变量:在函数中通过var声明的变量。

全局变量:在函数外通过var声明的变量。

没有声明就使用的变量,默认为全局变量,不论这个变量在哪被使用。

闭包

闭包是一种保护私有变量的机制,在函数执行时形成私有的作用域,保护里面的私有变量不受外界干扰。

直观的说就是形成一个不销毁的栈环境。

js代码app1:

(function () {
    var n = "ime";
    function People(name) {
        this._name = name;
    }
    People.prototype.say=function () {
        alert("hello"+this._name+n);
    }//say 为方法
    window.People =People;
}()) ;//n 只能在闭包里访问

(function () {

    function Student(name) {
        this._name = name;
    }
    Student.prototype =new People();// 继承People
    var superSay  = Student.prototype.say;
    Student.prototype.say = function () {
        superSay().call(this);
        alert("s-hello"+this._name+n);
    } //子类
    window.Student =Student;
}());
var s = new Student("iwen");
s.say();


js代码app2:

(function () {
    var n="ime";
        function Person(name) {
            var _this={};
            _this._name= name;
            _this.sayHello = function () {
                alert("hello"+_this._name+n);
            };
            return _this;
        }
    window.Person = Person; //开个窗口
    }());
function Teacher(name) {
    var _this= Person(name);
    var superSay= _this.sayHello;
    _this.sayHello=function () {
        superSay.call(_this);
        alert("thello"+_this._name);
    };
    return _this;
}
var t=Teacher("iwen");
t.sayHello();

效果图在这里插入图片描述

在这里插入图片描述

实例1:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
<div id="1">1</div>
<div id="2">2</div>
<div id="3">3</div>

<script type="text/javascript">
    var i;
    for(i=1;i<=3;i++){
        var ele = document.getElementById(i);
        ele.onclick=(function (id) {
                return function () {
                    alert(id);
                }
            })(i);
    }
</script>
</body>
</html>

在这里插入图片描述
点击相应数字会弹出对应的值,点1弹1,点2弹2.

js的工厂模式

工厂模式

考虑到在 ECMAScript 中无法创建类,开发人员就发明了一种函数,用函数来封装以特定接口创建对象的细节,如下面的例子所示:

  function createPerson(name,age,job){

    var o = new Object();

    o.name = name;

    o.age = age;

    o.job = job;

    o.sayName = function(){

      alert(this.name);

    }

    return o;

  }

  var person1 = createPerson('Grey',27,'Doctor');

函数 createPerson()能够根据接受的参数来构建一个包含所有必要信息的 Person 对象。可以无数次地调用这个函数,而每次它都会返回一个包含三个属性一个方法的对象。工厂模式虽然解决了创建\多个相似对象的问题,但却没有解决对象识别的问题(即怎样知道一个对象的类型)。

主要好处就是可以消除对象间的耦合,通过使用工程方法而不是new关键字。将所有实例化的代码集中在一个位置防止代码重复。

工厂模式解决了重复实例化的问题 ,但还有一个问题,那就是识别问题,因为根本无法 搞清楚他们到底是哪个对象的实例。

js 的prototype原型理解讲解

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在我理解看来,prototype不外乎就两个基础的作用:
1.动态的添加类的属性和方法
2.复制原类的内容

我们先来看第一个:
首先来定义一个简单的Person类;

var Person = function(name,age){
//指定该类的属性为参数值
this.name = name;
this.age = age;
//为该类指定一个talk方法
this.talk = function(){
alert("talk");
};
};

下面我们来生成Person 的实例

var zhangsan = new Person("zhangsan",19);
var lisi = new Person("lisi",19);
1
2

这样我们产生了两个Person 的实例,zhangsan 和lisi ,原先Person里有个方法talk,而我们生成实例的时候,该talk会被再次生成一遍,因此这是极其不好的,因为假如我们要产生100个Person 的实例,该talk()就会被创建100次,而这很有可能导致内存溢出。那有没有办法只创建一次talk()呢,答案是有的!就是用prototype!

先来看一段代码:


//照样是定义一个Person的类
var Person = function(name,age){
//指定该类的属性为参数值
this.name = name;
this.age = age;
//我们这里不再为Person设定talk方法
};
};

再次来生成Person 的实例

var zhangsan = new Person("zhangsan",19);
var lisi = new Person("lisi",19);

这里创建完毕后,我们得知,zhangsan和lisi都没有talk的方法,我们现在用prototype给他们加上

Person.prototype.talk = function(){
alert("talk");
};

此时,Person所有的实例都拥有了这个方法


zhangsan.talk();//"talk"
lisi,talk();//"talk"

既然第一种用处搞清楚了,现在来看第二种用处。
所谓第二种用处就是复制原类的内容。


//新建一个类Person2
var Person2 = function(name,age){};
Person2.prototype = new Person();

此时的Person里的内容已经全部拷贝到Person2里面了

This

this总是返回一个对象,即返回属性或方法当前所在的对象。

对象的属性可以赋给另一个对象,所以属性所在的当前对象是可变的,即this的指向是可变的。

样例:

var A = {

    name : '张三',
    describe : function(){
        return '姓名:' + this.name;
    }
};
var B = {
  name : '李四'
}

B.describe = A.describe;

B.describe();

结果:“姓名:李四”

再看一个例子:


var A = {

    name : '张三',
    describe : function(){
       return '姓名:' + this.name;
    }
};
var name = '李四'
f = A.describe;
f();

结果也是“姓名:李四”,因为这时this指向f运行时所在的对象——顶层window

this的使用场合

1、全局环境——无论this是不是在函数内部,只要是在全局环境下运行,this就是指顶层对象window

2、构造函数——指的是实例对象

eg:

var Obj = function(p){
    this.p = p;
}
Obj.prototype.a = function(){
    return this.p;
}
var obj = new Obj('color');
obj.a();
obj.p;

结果是都返回"color"

上面代码定义了一个构造函数Obj,由于this指向实例对象,所以在Obj中定义this.p,相当于定义实例对象有一个p属性,然后m方法可以返回这个p属性。

3、对象的方法

var obj = {
   foo : function(){
        console.log(this);
    }
};


obj.foo();//obj

只有直接在obj对象上调用foo方法,this才会指向obj,其他用法时,this都指向代码块当前所在的对象。

New 的自定义实现创建

new是怎么创建实例对象的;首先创建一个构造函数;

function Person(name,age){

this.name=name;

this.age=age;

};

var p=new Person(‘ck’,16)

通过new可以创建构造函数Person的实例对象。那么我们怎么去实现这一功能的呢?下面就为大家揭晓谜底:

function New(fn){

   return function(){

    var obj={'__proto__':fn.prototype};

    fn.apply(obj,arguments);

    return obj;

}

}

var p1=New(Person)('chen',22)

p1.name;//打印chen 

p1.age;//打印22

p1 instanceof Person // true 验证p1是否是Person的实例对象。 

这样我们就可以自定义创建一个实例对象了。

1、首先呢通过创建一个New函数,该函数接收一个参数(要创建实例对象的构造函数);

2、然后函数内部返回一个匿名函数

3、匿名函数内部返回一个对象,该对象就是构造函数的实例对象;那么这里有一个问题就是我们要使New函数可以通用,那么就要知道每一个构造函数的实例对象的属性;所以我在匿名函数的内部通过apply()方法用构造函数替换当前对象obj;同时将构造函数的执行上下文指向obj;故obj对象就继承了构造函数的相关属性。

这里解释下apply
apply:方法能劫持另外一个对象的方法,继承另外一个对象的属性.

Function.apply(obj,args)方法能接收两个参数
obj:这个对象将代替Function类里this对象
args:这个是数组,它将作为参数传给Function(args–>arguments)

     call:和apply的意思一样,只不过是参数列表不一样.

Function.call(obj,[param1[,param2[,…[,paramN]]]])
obj:这个对象将代替Function类里this对象
params:这个是一个参数列表

apply示例:

<script type="text/javascript">  
    /*定义一个人类*/  
    function Person(name,age)  
    {  
        this.name=name;  
        this.age=age;  
    }  
    /*定义一个学生类*/  
    functionStudent(name,age,grade)  
    {  
        Person.apply(this,arguments);  
        this.grade=grade;  
    }  
    //创建一个学生类  
    var student=new Student("zhangsan",21,"一年级");  
    //测试  
    alert("name:"+student.name+"\n"+"age:"+student.age+"\n"+"grade:"+student.grade);  
    //大家可以看到测试结果name:zhangsan age:21  grade:一年级  
    //学生类里面我没有给name和age属性赋值啊,为什么又存在这两个属性的值呢,这个就是apply的神奇之处.  
</script>  

分析: Person.apply(this,arguments);

this:在创建对象在这个时候代表的是student

arguments:是一个数组,也就是[“zhangsan”,”21”,”一年级”];

也就是通俗一点讲就是:用student去执行Person这个类里面的内容,在Person这个类里面存在this.name等之类的语句,这样就将属性创建到了student对象里面

附: typeof xxx:判断变量的数据类型; xxx instanceof Array:精确判断变量是否属于某一类型。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值