const对象的属性可以被修改吗?
因为对象是引用类型的,a保存的仅是对象的地址(指针),这就意味着,const仅保证地址(指针)不发生改变,修改对象的属性不会改变对象的地址(指针),所以是被允许的。也就是说const定义的引用类型只要指针不发生改变,其他的不论如何改变都是允许的。
const a = {name:'wangjianguo'}
a.name = "123"
console.log(a)
var、let、const区别
共同点:都可以声明变量。
区别一:
var具有变量提升的机制,let和const不具备变量提升的机制。
区别二:
var可以多次声明同一个变量。let和const 多次声明同一个变量会报错。
区别三:var和let声明变量的,const声明常量
var和let声明的变量可以再次赋值,但是const不可以再次赋值了。
区别四:var声明的变量没有自身作用域,let和const声明的变量有自身的作用域。
console.log(str) // undefined
var str = "你好"
console.log(num) // 报错
let num = 10
考题1:let和const不具备变量提升的机制。
console.log(str) // undefined
var str = "你好"
console.log(num) // 报错
let num = 10
考题2:let和const不具备变量提升的机制。
function demo (){
var n = 2;
if (true) {
var n = 1
}
console.log(n) // 1 (n 可以再次被声明)
}
function demo (){
let n = 2;
if (true) {
let n = 1 // 这里的n 的作用域仅限 if 条件大括号内
}
console.log(n) // 2 (此时访问的是外部的 n)
}
考题3:可以修改const常量里的属性
const obj = {a:1}
obj.a = 11111
cosole.log(obj) // {a:11111}
const arr = ['a','b','c']
arr[0] = 'aaaaa'
console.log(arr) // ['aaaaa','b','c']
将下列对象进行合并
const a = {a:1,b:4}
cosnt b = {b:2:c;3}
// 方式一 Object.assign
let obj1 = Object.assign(a,b)
console.log(obj1)
// 方式二 ... 扩展运算符
let obj2 = {...a,...b}
console.log(obj2)
// 方式三 : 自己封装方法
function extend(target,source){
for (var key in source){
target[key] = source[key]
}
return target
}
console.log(extend(a,b))
Object.freeze 和const的区别?
- Object.freeze()返回的是一个不可变的对象。这就意味着我们不能添加、删除或更改对象的任何属性
- const和Object.freeze()并不同,const是防止变量重新分配,而Object.freeze()是使对象具有不可变性。
Object.freeze() 只能冻结当前对象,如何手写实现深冻结
Object.freeze仅能冻结对象的当前层级属性,换而言之,如果对象的某个属性本身也是一个对象,那么这个内部对象并不会被Object.freeze冻结
const person = {prop1:99,prop2:'wangjianguo'}
person.prop1 = 100
console.log(person.prop1) // 100
//冻结对象
Object.freeze(person)
person.prop2 = 'hahaha'
consloe.log(person.prop2) // wangjianguo
手写深度冻结
function deepFreeze(obj){
//取回定义在obj上的属性名
var propNames = Object.getOwnPropertyNames(obj);
//在冻结自身之前冻结属性
propNames.forEach(fuction (name) {
var prop = obj[name]
// 如果prop是个对象,则进行冻结
if (typeof prop == 'object' && prop !== null) deepFreeze(obj)
})
//冻结自身
return Object.freeze(obj)
}
箭头函数
什么是箭头函数
var f = (v) => {
return v
}
//等同于
var f = function (v) {
return v
}
当参数只有一个时,可以省略参数的圆括号,函数体只有一条语句时,可以省略return, 返回值是对象时,需要用圆括号包括一下,因为JavaScript不知道你是返回的是对象还是一个代码块。
// 报错
let fun = id => {id:id,name:"temp"}
// 不报错
let fun = id => ({id:id,name:"temp"})
箭头函数的this
箭头函数不会创建自己的this,所以它没有自己的this,它只会在自己作用域的上一层继承this。所以箭头函数中this的指向它在定义时已经确定了,之后不会改变。
function foo(){
console.log(this)
setTimeout(() =>{
console.log("id:",this.id) // this 为 {id:42}
},100)
}
var id = 21
foo.call({id:42})
为什么箭头函数的this指向的上一层作用域的this?
//ES6
function foo(){
console.log(this)
setTimeout(() =>{
console.log("id:",this.id) // this 为 {id:42}
},100)
}
在ES5即以下是不支持箭头函数的,箭头函数的用babel 转换下的写法
//ES5 箭头函数运行在低版本会转换为这种写法
function foo(){
console.log(this)
var _this = this
setTimeout(function (){
console.log("id:",_this.id)
},100)
}
箭头函数的this在定义时就被确定了。换而言之,在使用时无法改变箭头函数的this。
var id = "Global"
let fun1 = () => {
console.log(this.id)
}
fun1() // "Global"
fun1.call({id:"Obj"}) // "Global"
fun1.apply({id:"Obj"}) // "Global"
fun1.bind({id:"Obj"})() // "Global"
function foo(){
return () =>{
return () =>{
return () =>{
console.log("id:",this.id)
}
}
}
}
var f = foo.call({id:1})
var t1 = f.call({id:2})()()
var t2 = f().call({id:2})()
var t3 = f()()call({id:2})
箭头函数可以让this指不可变,这种特性很有利于封装回调函数。下面是一个例子,DOM事件的回调函数封装在一个对象里面
var handler = {
id:"123456",
init:function(){
document.addEventListener("click",event => this.doSomething(event.type),false)
// this指向外层,指向init,init属性属于handler,所以this指向handler。
},
doSomething:function (type) {
console.log('Handling' + type + 'for' + this.id)
}
}
function foo() {
return () => {
return () => {
return () => {
console.log("id:",this.id)
}
}
}
}
var f = foo.call({id:1}) // 只有foo函数绑定有效。其他都没自己的this,还需要绑定吗,所以绑定无效。
var t1 = f.call({id:2})()() // 1
var t2 = f().call({id:3}) // 1
var t3 = f()().call({id:4}) //1
箭头函数的优点
更简洁更直观
function insert(value){
return {
into : fuction (array) {
return {
after :function (afterValue){
array.splice(array.indexOf(afterValue) + 1,0,value)
return array
}
}
}
}
}
// 箭头函数的写法
let insert = (value) => ({
into:(array) => ({
after:(afterValue) => {
array.splice(array.indexOf(afterValue) + 1,0,value)
return array
}
})
})
insert(2).into([1,3]).after(1) // [1,2,3]
箭头函数和普通函数区别
- this指向的问题。箭头函数中this只在箭头函数定义时就决定的,而且不可修改的(call,apply,bind)箭头函数的this指向定义时候,外层第一个普通函数
- 箭头函数不能new(不能当做构造函数)
- 箭头函数没有prototype
- 箭头函数没有arguments
let obj = {
a:function(){
console.log(this)
},
b:() =>{
console.log(this)
}
}
obj.a() // {a:f,b:f}
obj.b() // Window
class与构造函数的相似性、区别
构造函数
function Person (name,age){
this.name = name
this.age = age
}
// 原型方法
Person.prototype.sayName = function () {
console.log('我的名字是${this.name}')
}
// 静态方法
Person.staticFunction = function () {
console.log('我是静态方法')
}
var wangjianguo = new Person('wangjianguo',99)
wangjianguo.sayName() // 我的名字是wangjianguo
Person.staticFunction() // 我是静态方法
class
class Person {
constructor(name,age){
this.name = name
this.age = age
}
sayName() {
console.log('我的名字是${this.name}')
}
static staticFunction(){
console.log('我是静态方法')
}
}
var wjg = new Person('wangjianguo',99)
wjg.sayName() // 我的名字是wangjianguo
Person.staticFunction() // 我是静态方法
console.log(wjg.name) // wangjianguo
console.log(wjg.age) // 99
区别
- ES6中,class上的原型方法不可枚举;ES5中,构造函数上的原型方法可以枚举
function PersonES5 (name,age){
this.name = name
this.age = age
}
// 原型方法
PersonES5.prototype.sayName = function () {
console.log('我的名字是${this.name}')
}
// 静态方法
PersonES5.staticFunction = function () {
console.log('我是静态方法')
}
var personES5 = new PersonES5('es5example',25)
for (let prop in personES5){
console.log(prop) // 这里会打印‘name’,‘age’,以及‘sayname’
}
class Person {
constructor(name,age){
this.name = name
this.age = age
}
sayName() {
console.log('我的名字是${this.name}')
}
static staticFunction(){
console.log('我是静态方法')
}
}
var personES6 = new PersonES6('es6example',26)
for (let prop in personES6){
console.log(prop) // 这里仅打印‘name’,‘age’
}
- class必须通过new调用,普通构造函数不使用new 关键字,会当成普通函数调用,但class 没有经过new调用会抛出错误
TypeError:Class constructorPerson cannot - class上的原型方法不可作为构造函数
construct
什么是内部方法[[construct]]
[[Construct]]是JavaScript引擎的一个内部方法,主要用于创建和初始化对象。
我们不能直接访问它,而是JavaScript引擎在背后用来处理通过new关键字创建新对象的机制。
不是所有函数都可以作为构造函数,只有具有[[construct]]的函数才能作为构造函数。ES6中,class 定义的构造函数里的原型方法没有[[construct]]方法。不能用new关键字调用。
function isContructor(func) {
try {
Reflect.construct(Object,[],func)
} catch (e) {
return false
}
}
function Test1(){}
const Test2 = () => {}
console.log(isContructor(Test1)) // true
console.log(isContructor(Test2)) // false
构造函数内部定义方法和外部定义的方法区别?
class Person {
constructor(name){
this.name = name
this.say1 = () =>{
console.log("我在里面",this.name)
}
}
say2(){
console.log("我在外面",this.name)
}
}
const A = new Person("A")
const B = new Person("B")
A.say1() // 我在里面,A
A.say2() // 我在外面,A
console.log(A.__proto__.say1) // undefined
console.log(A.__proto__.say2) // f say2() {...}
console.log(A.say1 === B.say1) // false 实例方法是单独的
console.log(A.say2 === B.say2) // true 原型上的方法是共享的
Object.keys(A) // ["name","say1"]
第一点:
在构造函数(constructor)内部定义的方法(例如say1)实际上是在每个对象实例上创建了一个新的函数
在构造函数(constructor)外部定义的方法(例如say1)是在Person的原型对象上(Person.prototype)创建的
第二点:
在构造函数(constructor)内部定义的方法(例如say1)是各个实例对象独有的。
在构造函数(constructor)外部定义的方法(例如say2),所有Person实例共享的的。
第三点:
在构造函数(constructor)内部定义的方法可以被Object.keys()遍历
在构造函数(constructor)外部定义的方法不能被Object.keys()遍历