自定义类
使用this关键字修饰的变量不再是局部变量,它是该函数的实例属性
<script>
function Student(name, age, sex) {
this.name = name;
this.age = age;
this.sex = sex;
}
let bb = new Student("胖子", 18, true);
console.log(bb)
let ss = bb.name + "," + bb.age + "," + (bb.sex ? '男' : '女');
console.log(ss);
//更加簡化的定義方式
var pan = {};
//添加屬性
pan.name = "綠毛";
pan.age = 12;
pan.sex = false;
console.log(pan);
</script>
<script>
function Person(name, age) {
//实例对象每个实例都不同,可以通过new Person("",19).name的方式访问
this.name = name;
//类属性,是类的所有实例公用,只能通过Person.nation的方式访问,
//而不能使用new Person("yan",18).nation方式访问
// Person.nation = "汉族";
Person.nation=age;
var bb = 0;//局部变量,外面不能访问,类似局部函数
}
let p1=new Person("zhangsan","白族");//new Person;
let p2=new Person("黃毛","蟲族");
console.log(p1);
console.log(p2);
console.log(Person.nation);
//注意:局部变量在函数执行完毕后销毁,全局变量在页面
//关闭后销毁,函数内没有使用var声明的变量为全局变量
//直接定义一个对象
var p = { //JSON格式定義
//this.pp=function(){}可以用於function p(){}定義中
pp: function () {
for (var k = 0; k < 10; k++)
document.writeln("慢慢的走...");
}
}
//调用方式
p.pp();
</script>
Js中的函数
函数就像java方法一样,这个函数可以被调用
定义一个函数时,系统也会创建一个对象,这个对象就是Function类的实例
定义一个函数时,这个函数通常都会附加给某个对象,作为对象的方法。没有明确指出将函数附加到哪个对象上时,该函数将附加到window对象上,作为window对象的方法。
<script>
function ff1(){
document.write('ff1()');
}
console.log(window);
window.ff1();
</script>
定义函数时也得到了一个与函数同名的类,该函数就是该类的唯一的构造器。
所以实际上函数有直接调用和使用new关键字调用两种方式
<script>
function Student(){
alert("Student");
}
//Student();直接調用
new Student(); //將function定義當做構造器使用
</script>
js的运行机制
- 在js中js引擎会优先解析var变量和function定义,在预解析完成后从上到下逐步进行
- 解析var变量时会把值存储在执行环境中,而不会去赋值,值是存储作用!如alert(a);
var a = 5;这时会输出undifiend,意思是没有被初始化没有被赋值。这并不是没有被定义,错误的意思 - 在解析function时会把函数整体定义,这也就解释了为什么在function定义函数时为什么可以先调用后声明了!其实表面上看是先调用了,其实在内部机制中第一步实行的是把以function方式定义的函数先声明了!
<script>
name="zhangsan";
function ff(){
alert(name);
// var name=123;
let name=999;
}
ff();
</script>
ES6中箭头函数的调用
箭头函数表达式的语法比函数表达式更简洁,并且没有自己的this,arguments,super或 new.target。这些函数表达式更适用于那些本来需要匿名函数的地方,并且它们不能用作构造函数
(参数1, 参数2, …, 参数N) => { 函数声明 }
相当于:(参数1, 参数2, …, 参数N) =>{ return 表达式; }
(参数1, 参数2, …, 参数N) => 表达式(单一语句)
当只有一个参数时,圆括号是可选的:
(单一参数) => {函数声明}
单一参数 => {函数声明}
没有参数的函数应该写成一对圆括号。
() => {函数声明}
<script>
//es5中:
var x = 11;
var obj = {
x: 22,
say: function () {
console.log(this.x)
}
}
obj.say(); //22
//而在es6中
var x = 11;
var obj = {
x: 22,
say: () => {
console.log(this.x);
}
}
obj.say(); //11
</script>
<script>
var bb = {
pp: function () {
console.log(this); //this是Bb对象
}
}
bb.pp();
var obj = {
pp: () => {
console.log(this);//是window,而不是obj对象 [重点]
}
/*
- 箭头函数不可以当作构造函数使用,也就是不能用new命令实例化一个对象,
否则会抛出一个错误
- 箭头函数的this是和定义时有关和调用无关,无论使用apply还是call都无
法改变
- 调用就是函数调用模式
*/
}
obj.pp();
</script>
<script>
(() => {
console.log(this)//window
})()
let arrowFun = () => {
console.log(this)//window
}
arrowFun()
let arrowObj = {
arrFun: function () {
(() => {
console.log(this)//arrowObj
})()
}
}
arrowObj.arrFun();
</script>
原型对象
<script>
function Student(){
this.name="name";
this.pp=function(){
alert(this.name+"---"+age);
}
var age=100; //只能在当前函数内部进行访问,其它地方不能访问,即使使用prototype原型
}
var s1=new Student();
s1.abc=function(){
alert("s1....abc");
}
Student.prototype.abc=function(){
console.log(this.name);
// console.log(age);因为age是临时变量
return "ppppp";
}
var s2=new Student();
// alert(s2.abc());
s2.pp();
</script>
外部读取局部变量
<script>
function Student(){
function abc(){
return kk;
}
var kk=999;
return abc;
}
var s=new Student();
alert(s.kk); //undefined
alert(s()); //999
</script>
类基础定义
<script>
function Student() {
this.toString=function(){
return "xxxx";
}
}
var s1 = new Student();
s1.name = "胖子";
s1.age = 19;
// alert(s1);
console.log(s1);
//覆盖定义
s1.toString=function(){
return this.name+"---"+this.age;
}
console.log(s1.name + "---" + s1['name']);
console.log(s1.toString());
/*
访问对象公开属性的方式有两种:
- 对象名.属性名
- 对象名['属性名']
*/
</script>
局部变量和局部函数
在函数内部定义的变量称为局部变量,在函数外部定义的变量称为全局变量
局部函数是指在函数中定义的函数
<script>
function ff3(){
alert("ff3"+bb);
var bb=123;
}
ff3();
</script>
<script>
var bb="abcd";
function ff1(){
var bb=999;
alert("ff1"+bb);
}
ff1();
</script>
<script>
function ff2(){
alert("ff2"+bb);
}
ff2();
</script>
<script>
function nn1(){
nn2();
function nn2(){
alert("nn2");
}
alert("nn1");
}
nn1();
nn2(); //nn2只能在nn1的內部進行調用
</script>
<script>
function nn3(){
function nn4(){
alert("nn4");
}
return nn4; //允許返回當前function中定義的內部function
}
nn3()(); //調用nn4();
</script>
<script>
function outer(){
function inner(){//局部函数
document.writeln("内部函数1111<br/>");
}
document.writeln("测试局部函数.....开始");
inner();
document.writeln("测试局部函数.....结束");
}
//在全局函数outer中定义了一个局部函数inner,在outer中调用没有问题,但是在outer之外则无法访问
outer();
</script>
函数
js是一种基于对象的脚本语言,代码复用的范围是函数,函数可以独立存在。
函数的最大作用是提供代码复用,将需要重复使用的代码块定义为函数,提供更好的代码复用
编写JS的流程
布局:html+css 在写js之前必须保证有一个稳固的布局,这个布局本身不能有任何兼容问题
属性:确定要修改哪些属性 确定通过js修改哪些属性,例如display
事件:确定用户做哪些操作(产品设计)确定要在什么样的事件里修改,比如点击、移入移出
编写js:在事件中,用js来修改页面元素的样式
原始数据
<button onclick="ff()" id="btn1">隐藏</button>
<script>
function ff() {
document.getElementById("div1").className = "bb1";
document.getElementById("btn1").innerText = "显示";
document.getElementById("btn1").onclick = dd;
}
function dd() {
document.getElementById("div1").className = "bb2";
document.getElementById("btn1").innerText = "隐藏";
document.getElementById("btn1").onclick = ff;
}
</script>
<style>
.bb1 {
display: none;
}
.bb2 {
display: block;
}
</style>
<label onmouseover="document.getElementById('btn1').style.display='block'"
onmouseout="document.getElementById('btn1').style.display='none'">
鼠标经过出现按钮</label>
定义函数的3种方式
三种方法的对比
- 函数声明有预解析,而且函数声明的优先级高于变量
- 使用Function构造函数定义函数的方式是一个函数表达式,这种方式会导致解析两次代码,影响性能。第一次解析常规的JavaScript代码,第二次解析传入构造函数的字符串
命名函数
function hello(name){
alert(‘欢迎您,’+name+",你好!");
}
在同一个script标签中允许先调用函数,然后再定义函数;在不同的script中要求必须先定义函数,然后再调用函数
<script>
h1("猴子");
function h1(name) {
alert('Hello ' + name + "!")
}
function h2(name) {
alert('Hello ' + name + "!")
}
</script>
<script>
h2("胖子");
</script>
定义匿名函数
定义匿名函数无需指定函数名,而是将参数列表紧跟在function关键字后
匿名函数有很好的可读性。注意js函数很特殊,即使可重复调用的代码块,也是一个Function实例
<script>
var f=function(name){
document.writeln('匿名函数!<br/>');
document.writeln('你好,'+name);
};
f('yanjun');
//語法錯誤
/*
function(){
alert('ddddd');
}
*/
</script>
使用Function类匿名函数
<script>
var f=new Function('name',"document.writeln('Function定义的函数<br/>');"
+ "document.writeln('你好'+name);");
f("小明");
</script>
方法覆盖
看代码
<script>
function Student(){ //构造器
this.name="猴子";
this.sleep=function(){
return "我要找母猴子";
}
}
var a=new Student;
console.log(a.name);
//在对象中进行方法覆盖
a.sleep=function(){
return "我爱母猴子";
}
console.log(a.sleep());
var b=new Student;
console.log(b.sleep());
console.log(Student.prototype)
//通过原型对象可以新增加方法,但是不能实现方法的覆盖定义
Student.prototype.sleep1=function(){
return "猴子爱桃子";
}
console.log(a.sleep());
var c=new Student();
console.log(c.sleep());
console.log(Student.prototype)
//js函数不支持重载
</script>
调用函数
调用函数的四种方式
作为一个函数去调用【函数名()】(函数作为全局对象调用,会使this的值成为全局对象,使用window对象作为一个变量,容易造成程序崩溃!) ,例如alert(’’)
函数作为方法调用:(函数作为对象的方法调用,会使this的值成为对象的本身!),例如window.alert(’’)
使用构造函数调用函数:(构造函数中的this指向当前对象) new Date()
作为函数方法间接调用函数pp()();
函数直接调用
<script>
function ff1() {
this.name = "abc";
console.log(this);
}
// console.log(this)
// ff1();
</script>
对象方法调用
<script>
var objList = {
name: 'methods',
getSum: function () {
console.log(this) //objList对象
}
}
objList.getSum()
console.log(this);
</script>
构造器调用
<script>
function Person() {
console.log(this); //指向构造函数Person
}
var personOne = new Person();
</script>
间接调用
利用call和apply来实现,this就是call和apply对应的第一个参数,如果不传值或者第一个值为null、undefined时this指向window
<script>
function foo() {
console.log(sss)
console.log(this);
}
foo.apply('我是apply改变的this值');//我是apply改变的this值
foo.call('我是call改变的this值');//我是call改变的this值
function f1() {
alert("f1.....")
}
f1("abcd");
</script>
<script>
window.color = 'red';
document.color = 'yellow';
var s1 = { color: 'blue' };
function changeColor() {
console.log(this.color);
}
//通过第一个参数用于指定所调用函数中的this是谁
//changeColor()
changeColor.call(); //red (默认传递参数)
//window.changeColor();
changeColor.call(window); //red
//document.changeColor();
changeColor.call(document); //yellow
//this.changeColor();
changeColor.call(this); //red
//s1.changeColor();
changeColor.call(s1); //blue,劫持了函数中的this,使this为s1对象
</script>
以apply方法调用
call调用函数时必须在括号中详细列出每个参数
apply动态调用函数时,可以在括号中以arguments代表所有的参数
call和apply用法
间接调用函数,改变作用域的this值
劫持其他对象的方法
<script>
function f9(){
console.log(this.getYear());
}
f9.call(new Date);
</script>
因为javascript中没有class类,所以有人把类也称为原型对象,因为这两个概念从在编程中发挥的作用看都是一个意思,为了统一叫法,这里就统一叫类
闭包
函数定义在另外一个函数内部可以达到限制访问的目的
<script>
function func() {
var num = 0; //声明局部变量:num
function f() { //嵌套函数,在作用域里
console.log(++num)
}
return f; //调用嵌套函数f,并将f的执行结果返回
}
var ff = func();
window.ff(); //输出:1
ff(); //输出:2
ff(); //输出:3
</script>
函数对象可以通过作用域链相互关联起来,函数体内部的变量都可以保存在函数作用域内。
通常说的闭包是当一个函数嵌套另一个函数,外部函数将嵌套函数对象作为返回值返回的时候,我们把这种情况称为闭包
当一个函数func()创建后,它保存了一个作用域链,并且作用域链会被函数func()中的作用域中可访问的数据对象num填充
执行此函数func()时会创建一个称为“运行期上下文(execution context)”的内部对象,运行期上下文定义了函数执行时的环境。并且运行期上下文对应自己的作用域链,同时它的作用域链
初始化为当前运行函数func()的[[Scope]]所包含的对象,然后执行环境会创建一个活动对象(call object),用来报讯局部变量,并把这个对象添加至作用域链[[Scope]]中。假如不存在嵌套函数,也没有其他引用的时候,活动对象就会被当做垃圾回收掉。但是这里有嵌套函数f(),并将函数f()作为返回值返回时,就会又一个外部引用指向这个嵌套函数f(),活动对象就不会当做垃圾回收掉,并且活动对象指向的变量所绑定的对象也不会被回收。
在实际开发中,闭包主要是用来封装变量实现公有私有变量,收敛权限.
优点:
- 变量长期驻扎在内存中
- 避免全局变量的污染
- 私有成员的存在
缺点:
- 因为不会被GC回收,所以常驻内存,会增大内存的使用量,使用不当会造成内存泄露。