Apps Script that will create a Google Form, turn it into a quiz

πŸ“Œ How to use

  1. Go to script.google.com β†’ New project.
  2. Paste this entire script into Code.js.
  3. Run buildJavaScriptQuizForm() once and authorize.
  4. Check the Logs for the edit URL and live URL of the new quiz.
/**
 * Builds a 100-question JavaScript multiple-choice quiz as a Google Form.
 */
function buildJavaScriptQuizForm() {
  const formTitle = 'JavaScript 100-Question Quiz';
  const form = FormApp.create(formTitle)
    .setDescription('100-question JavaScript multiple-choice quiz with explanations.')
    .setIsQuiz(true);

  const QUESTIONS = [
    {
      question: 'What does typeof 42 return?',
      choices: ['"int"', '"number"', '"integer"', '"numeric"'],
      correctIndex: 1,
      explanation: 'In JavaScript, all numeric values (integers and floats) have the type "number".'
    },
    {
      question: 'What is the result of typeof null?',
      choices: ['"null"', '"undefined"', '"object"', '"value"'],
      correctIndex: 2,
      explanation: 'typeof null returns "object" because of a long-standing historical quirk in JavaScript.'
    },
    {
      question: 'What will console.log(1 + "2") output?',
      choices: ['3', '"3"', '"12"', 'NaN'],
      correctIndex: 2,
      explanation: 'When adding a number and a string, JavaScript coerces the number to a string and concatenates, giving "12".'
    },
    {
      question: 'What does NaN stand for?',
      choices: ['Not a Null', 'Not a Number', 'Negative a Number', 'No actual Number'],
      correctIndex: 1,
      explanation: 'NaN is the special numeric value that represents an invalid number result, such as 0 / 0.'
    },
    {
      question: 'Which of these is NOT a primitive type in JavaScript?',
      choices: ['string', 'boolean', 'object', 'symbol'],
      correctIndex: 2,
      explanation: 'object is not a primitive. Primitive types include string, number, boolean, null, undefined, symbol, and bigint.'
    },
    {
      question: 'What will typeof undefined return?',
      choices: ['"null"', '"undefined"', '"object"', '"void"'],
      correctIndex: 1,
      explanation: 'The primitive value undefined has the type "undefined".'
    },
    {
      question: 'Which operator is used for strict equality comparison?',
      choices: ['==', '=', '===', '=>'],
      correctIndex: 2,
      explanation: '=== compares both value and type with no type coercion.'
    },
    {
      question: 'What is the result of "5" == 5?',
      choices: ['true', 'false', 'NaN', 'Throws an error'],
      correctIndex: 0,
      explanation: '== performs type coercion, converting "5" to number 5, so the comparison is true.'
    },
    {
      question: 'What is the result of "5" === 5?',
      choices: ['true', 'false', 'NaN', 'Throws an error'],
      correctIndex: 1,
      explanation: '=== requires both type and value to be the same; string "5" is not equal to number 5.'
    },
    {
      question: 'Which keyword declares a block-scoped variable?',
      choices: ['var', 'let', 'const', 'Both let and const'],
      correctIndex: 3,
      explanation: 'Both let and const are block scoped, while var is function scoped.'
    },
    {
      question: 'What happens if you access a variable declared with let before its declaration?',
      choices: ['Returns undefined', 'Throws a ReferenceError', 'Returns null', 'Returns an empty string'],
      correctIndex: 1,
      explanation: 'let and const are in the temporal dead zone before declaration and cause a ReferenceError if accessed.'
    },
    {
      question: 'What will this code log: console.log(a); var a = 10;',
      choices: ['10', 'undefined', 'null', 'ReferenceError'],
      correctIndex: 1,
      explanation: 'var declarations are hoisted and initialized with undefined, so the log shows undefined.'
    },
    {
      question: 'What will this code log: console.log(b); let b = 10;',
      choices: ['10', 'undefined', 'null', 'ReferenceError'],
      correctIndex: 3,
      explanation: 'Accessing a let variable before declaration produces a ReferenceError due to the temporal dead zone.'
    },
    {
      question: 'What is a closure?',
      choices: [
        'A way to close open files',
        'A function bundled with its lexical environment',
        'A function that always returns another function',
        'A method to end a program'
      ],
      correctIndex: 1,
      explanation: 'A closure is when a function retains access to variables from its outer scope even after that scope has finished.'
    },
    {
      question: 'Which array method adds one or more elements to the end of an array?',
      choices: ['push()', 'pop()', 'shift()', 'unshift()'],
      correctIndex: 0,
      explanation: 'push() appends elements to the end of an array.'
    },
    {
      question: 'Which array method creates a new array without modifying the original?',
      choices: ['push()', 'splice()', 'slice()', 'pop()'],
      correctIndex: 2,
      explanation: 'slice() returns a new array containing a selected portion, leaving the original unchanged.'
    },
    {
      question: 'What is the result of [1, 2, 3].length?',
      choices: ['2', '3', '4', 'undefined'],
      correctIndex: 1,
      explanation: 'The length property of an array is the number of elements, which is 3 here.'
    },
    {
      question: 'Which method converts a JSON string into an object?',
      choices: ['JSON.encode()', 'JSON.parse()', 'JSON.stringify()', 'JSON.toObject()'],
      correctIndex: 1,
      explanation: 'JSON.parse() converts a JSON string into a JavaScript object.'
    },
    {
      question: 'What is the output of console.log(0 == false)?',
      choices: ['true', 'false', 'NaN', 'Throws error'],
      correctIndex: 0,
      explanation: '== coerces false to 0, so 0 == false is true.'
    },
    {
      question: 'What is the output of console.log(0 === false)?',
      choices: ['true', 'false', 'NaN', 'Throws error'],
      correctIndex: 1,
      explanation: '0 (number) and false (boolean) are different types, so === returns false.'
    },
    {
      question: 'How do you write an arrow function that returns the square of x?',
      choices: ['x => { x * x }', 'x => x * x', '(x) => return x * x', 'x -> x * x'],
      correctIndex: 1,
      explanation: 'For single-expression arrow functions, you can omit braces and return: x => x * x.'
    },
    {
      question: 'What will console.log(typeof (() => {})) output?',
      choices: ['"function"', '"object"', '"arrow"', '"undefined"'],
      correctIndex: 0,
      explanation: 'Arrow functions are still functions in JavaScript, so typeof returns "function".'
    },
    {
      question: 'What does Array.isArray(value) do?',
      choices: [
        'Checks if value is iterable',
        'Checks if value is an array',
        'Checks if value is an object',
        'Converts value to an array'
      ],
      correctIndex: 1,
      explanation: 'Array.isArray() returns true only when the value is an array.'
    },
    {
      question: 'What is the value of Number("hello")?',
      choices: ['"hello"', 'NaN', '0', 'undefined'],
      correctIndex: 1,
      explanation: 'Converting a non-numeric string to a number yields NaN.'
    },
    {
      question: 'Which of these is falsy?',
      choices: ['"0"', '[]', '{}', '""'],
      correctIndex: 3,
      explanation: 'The empty string "" is falsy. "0", [], and {} are truthy.'
    },
    {
      question: 'Which keyword stops a loop immediately?',
      choices: ['exit', 'stop', 'break', 'return'],
      correctIndex: 2,
      explanation: 'break exits the nearest loop or switch statement.'
    },
    {
      question: 'Which keyword skips the current iteration of a loop and continues with the next?',
      choices: ['skip', 'next', 'continue', 'pass'],
      correctIndex: 2,
      explanation: 'continue ends the current iteration and proceeds with the next one.'
    },
    {
      question: 'What is the result of console.log("5" - 2)?',
      choices: ['"52"', '3', 'NaN', '"3"'],
      correctIndex: 1,
      explanation: 'The - operator coerces strings to numbers, so "5" - 2 becomes 5 - 2, which is 3.'
    },
    {
      question: 'What is the result of console.log("5" + 2)?',
      choices: ['"52"', '7', 'NaN', '"7"'],
      correctIndex: 0,
      explanation: 'The + operator with a string performs concatenation, giving "52".'
    },
    {
      question: 'In a browser, what is window?',
      choices: [
        'A built-in array',
        'The global object for browser JavaScript',
        'A reserved name for DOM nodes',
        'A CSS object'
      ],
      correctIndex: 1,
      explanation: 'In browsers, the global scope is represented by the window object.'
    },
    {
      question: 'In strict mode, what happens if you assign to an undeclared variable?',
      choices: [
        'It creates a global variable',
        'It creates a local variable',
        'It throws a ReferenceError',
        'It is silently ignored'
      ],
      correctIndex: 2,
      explanation: 'Strict mode prevents implicit globals and throws a ReferenceError instead.'
    },
    {
      question: 'How do you enable strict mode in a script file?',
      choices: [
        'enable strict;',
        '"use strict"; at the top',
        'use strict; without quotes',
        'strict_mode(true);'
      ],
      correctIndex: 1,
      explanation: 'The directive "use strict"; at the top of a file or function enables strict mode.'
    },
    {
      question: 'What is the output of console.log([] == false)?',
      choices: ['true', 'false', 'NaN', 'Throws error'],
      correctIndex: 0,
      explanation: '[] is coerced to "" then to 0, false is also coerced to 0, so == returns true.'
    },
    {
      question: 'What is the value of typeof NaN?',
      choices: ['"nan"', '"number"', '"undefined"', '"object"'],
      correctIndex: 1,
      explanation: 'NaN is a special numeric value, so typeof NaN is "number".'
    },
    {
      question: 'How do you check if a value is NaN without coercion?',
      choices: [
        'value == NaN',
        'value === NaN',
        'isNaN(value)',
        'Number.isNaN(value)'
      ],
      correctIndex: 3,
      explanation: 'Number.isNaN(value) checks specifically for NaN and does not coerce the argument.'
    },
    {
      question: 'What is the result of console.log([] + [])?',
      choices: ['[]', '""', '"[]"', 'NaN'],
      correctIndex: 1,
      explanation: 'Arrays are converted to strings; [].toString() is "", so "" + "" is an empty string.'
    },
    {
      question: 'What does Array.prototype.map() return?',
      choices: ['A new array', 'The modified original array', 'A number', 'An object with keys and values'],
      correctIndex: 0,
      explanation: 'map() returns a new array of transformed elements and does not mutate the original array.'
    },
    {
      question: 'Which method is best for selecting elements that meet a condition?',
      choices: ['forEach()', 'map()', 'filter()', 'reduce()'],
      correctIndex: 2,
      explanation: 'filter() returns a new array containing only elements for which the callback returns true.'
    },
    {
      question: 'What does Array.prototype.forEach() return?',
      choices: ['A new array', 'The original array', 'undefined', 'The number of iterations'],
      correctIndex: 2,
      explanation: 'forEach() is used for side effects and always returns undefined.'
    },
    {
      question: 'What does Array.prototype.reduce() typically do?',
      choices: [
        'Sorts an array',
        'Flattens nested arrays only',
        'Reduces an array to a single value',
        'Filters elements'
      ],
      correctIndex: 2,
      explanation: 'reduce() combines elements using an accumulator to produce a single output value.'
    },
    {
      question: 'Which operator spreads the elements of an iterable?',
      choices: ['...', '*', '&', '=>'],
      correctIndex: 0,
      explanation: 'The spread syntax ... expands elements of an iterable in array literals, function calls, and so on.'
    },
    {
      question: 'What is destructuring in JavaScript?',
      choices: [
        'Removing properties from objects',
        'Assigning properties or elements from objects or arrays into variables',
        'Deleting variables',
        'Breaking a loop'
      ],
      correctIndex: 1,
      explanation: 'Destructuring lets you extract values from arrays or objects into variables with compact syntax.'
    },
    {
      question: 'Which is valid array destructuring?',
      choices: [
        'let {a, b} = [1, 2];',
        'let [a, b] = [1, 2];',
        'let (a, b) = [1, 2];',
        'let [a: 1, b: 2];'
      ],
      correctIndex: 1,
      explanation: 'Array destructuring uses square brackets, for example let [a, b] = [1, 2];'
    },
    {
      question: 'Which is valid object destructuring?',
      choices: [
        'let [name] = { name: "Max" };',
        'let {name} = { name: "Max" };',
        'let (name) = { name: "Max" };',
        'let {name: "Max"};'
      ],
      correctIndex: 1,
      explanation: 'Object destructuring uses curly braces with property names, for example let {name} = obj;'
    },
    {
      question: 'In non strict mode, what is the default value of this inside a regular function call fn() in a browser?',
      choices: ['undefined', 'The global object (window)', 'The function itself', 'null'],
      correctIndex: 1,
      explanation: 'In non strict mode, this in a plain function call refers to the global object (window in browsers).'
    },
    {
      question: 'In an arrow function, this is:',
      choices: [
        'Dynamically bound',
        'Always window',
        'Lexically inherited from the surrounding scope',
        'Always undefined'
      ],
      correctIndex: 2,
      explanation: 'Arrow functions do not have their own this; they capture this from the enclosing scope.'
    },
    {
      question: 'How do you create a new object using a constructor function Person?',
      choices: ['Person()', 'new Person()', 'create Person()', 'Object(Person)'],
      correctIndex: 1,
      explanation: 'The new keyword creates a new object and calls the constructor Person with this bound to that object.'
    },
    {
      question: 'Which prototype is used when calling a method on an array literal like []?',
      choices: ['Object.prototype', 'Array.prototype', 'Function.prototype', 'Prototype.prototype'],
      correctIndex: 1,
      explanation: 'Arrays inherit methods such as push and map from Array.prototype.'
    },
    {
      question: 'What does Object.create(proto) do?',
      choices: [
        'Copies all properties deeply from proto',
        'Creates an object whose internal prototype is proto',
        'Clones proto and adds new methods',
        'Creates a new class from proto'
      ],
      correctIndex: 1,
      explanation: 'Object.create(proto) returns a new object whose [[Prototype]] is proto.'
    },
    {
      question: 'How do you define a class in modern JavaScript?',
      choices: [
        'class Person {}',
        'function class Person {}',
        'Person class {}',
        'new class Person {}'
      ],
      correctIndex: 0,
      explanation: 'ES6 introduced the class syntax: class Person {}.'
    },
    {
      question: 'How do you define a method inside a class?',
      choices: [
        'methodName: function() {}',
        'function methodName() {}',
        'methodName() {}',
        'let methodName() {}'
      ],
      correctIndex: 2,
      explanation: 'Inside a class body, methods are defined as methodName() { } without the function keyword.'
    },
    {
      question: 'How do you create a subclass from a class Parent?',
      choices: [
        'class Child: Parent {}',
        'class Child extends Parent {}',
        'class Child inherits Parent {}',
        'class Child Parent {}'
      ],
      correctIndex: 1,
      explanation: 'Child extends Parent is the ES6 syntax for class inheritance.'
    },
    {
      question: 'Which keyword calls the parent class constructor?',
      choices: ['this()', 'parent()', 'base()', 'super()'],
      correctIndex: 3,
      explanation: 'super() calls the constructor of the parent class from within a subclass.'
    },
    {
      question: 'What does typeof function() {} return?',
      choices: ['"object"', '"function"', '"callable"', '"method"'],
      correctIndex: 1,
      explanation: 'Regular functions have type "function" when evaluated with typeof.'
    },
    {
      question: 'Which statement about promises is true?',
      choices: [
        'A promise can be pending, fulfilled, or rejected',
        'A promise can only be fulfilled',
        'Promises block the main thread',
        'Promises replace all callbacks completely'
      ],
      correctIndex: 0,
      explanation: 'Promises have three main states: pending, fulfilled, and rejected.'
    },
    {
      question: 'What does Promise.resolve(5) create?',
      choices: [
        'A rejected promise with value 5',
        'A fulfilled promise with value 5',
        'A pending promise with value 5',
        'A synchronous value 5 only'
      ],
      correctIndex: 1,
      explanation: 'Promise.resolve(5) returns a promise that is immediately fulfilled with value 5.'
    },
    {
      question: 'How do you attach a success handler to a promise p?',
      choices: ['p.then(onFulfilled)', 'p.success(onFulfilled)', 'p.done(onFulfilled)', 'p.resolve(onFulfilled)'],
      correctIndex: 0,
      explanation: 'then registers callbacks for fulfilled and optionally rejected states.'
    },
    {
      question: 'How do you handle errors in a promise chain?',
      choices: ['With try/catch only', 'With .catch()', 'With .error()', 'With .fail()'],
      correctIndex: 1,
      explanation: 'catch handles rejected promises and errors thrown in previous then handlers.'
    },
    {
      question: 'Which keyword is used to declare an asynchronous function?',
      choices: ['sync', 'await', 'async', 'defer'],
      correctIndex: 2,
      explanation: 'Prefixing a function with async makes it return a promise and allows await inside it.'
    },
    {
      question: 'What does await do inside an async function?',
      choices: [
        'Pauses the whole program',
        'Pauses only that async function until the promise settles',
        'Converts a promise to a callback',
        'Makes the function synchronous'
      ],
      correctIndex: 1,
      explanation: 'await suspends the async function until the awaited promise resolves or rejects, without blocking the event loop.'
    },
    {
      question: 'What happens if an error is thrown inside an async function?',
      choices: [
        'It crashes the browser',
        'It becomes a rejected promise',
        'It is ignored',
        'It becomes a fulfilled promise'
      ],
      correctIndex: 1,
      explanation: 'Errors thrown in async functions reject the returned promise, which can be caught with catch or try/catch around await.'
    },
    {
      question: 'What does setTimeout(fn, 0) do?',
      choices: [
        'Executes fn immediately',
        'Schedules fn after the current call stack clears',
        'Blocks execution until fn completes',
        'Throws an error'
      ],
      correctIndex: 1,
      explanation: 'setTimeout with 0 ms delays fn until after the current stack and microtasks complete.'
    },
    {
      question: 'Which of these is NOT part of the JavaScript language itself but provided by the browser?',
      choices: ['Array', 'Promise', 'document', 'Object'],
      correctIndex: 2,
      explanation: 'document is part of the DOM API, which is provided by browsers, not the core JS language.'
    },
    {
      question: 'How do you select an element with id "main" in the DOM?',
      choices: [
        'document.getElement("main")',
        'document.getElementById("main")',
        'document.query("#main")',
        'document.id("main")'
      ],
      correctIndex: 1,
      explanation: 'getElementById selects the element whose id matches the given string.'
    },
    {
      question: 'Which method selects the first element matching a CSS selector?',
      choices: [
        'document.querySelector()',
        'document.querySelectorAll()[0] only',
        'document.getElementBySelector()',
        'document.selectFirst()'
      ],
      correctIndex: 0,
      explanation: 'querySelector returns the first matching element or null.'
    },
    {
      question: 'How do you add a click event listener to a button element btn?',
      choices: [
        'btn.on("click", fn)',
        'btn.click(fn)',
        'btn.addEventListener("click", fn)',
        'btn.addClick(fn)'
      ],
      correctIndex: 2,
      explanation: 'addEventListener is the standard way to attach event listeners to DOM elements.'
    },
    {
      question: 'What will document.querySelectorAll(".item") return?',
      choices: ['A single element', 'An array', 'A NodeList', 'A string'],
      correctIndex: 2,
      explanation: 'querySelectorAll returns a NodeList, which is an array-like collection of elements.'
    },
    {
      question: 'Which property changes the text inside an element?',
      choices: ['innerText', 'text', 'content', 'valueText'],
      correctIndex: 0,
      explanation: 'innerText (or textContent) is used to set or get the text inside an element.'
    },
    {
      question: 'How do you prevent a form default submit behavior in an event handler?',
      choices: ['event.stop()', 'event.preventDefault()', 'event.cancel()', 'event.stopPropagation()'],
      correctIndex: 1,
      explanation: 'preventDefault stops the default browser action such as submitting a form.'
    },
    {
      question: 'What does event.stopPropagation() do?',
      choices: [
        'Prevents default browser behavior',
        'Stops the event from bubbling up to parent elements',
        'Disables all event listeners',
        'Cancels form submission only'
      ],
      correctIndex: 1,
      explanation: 'stopPropagation prevents the event from propagating to ancestor elements in the DOM.'
    },
    {
      question: 'What is hoisting?',
      choices: [
        'Moving files to the top of a project',
        'JavaScript moving declarations to the top of their scope before execution',
        'Sorting variables alphabetically',
        'Loading external scripts earlier'
      ],
      correctIndex: 1,
      explanation: 'Hoisting is the behavior where declarations appear to be moved to the top of their scope.'
    },
    {
      question: 'Which are hoisted with their full definitions?',
      choices: [
        'Function declarations',
        'Function expressions',
        'Arrow functions assigned to variables',
        'Both function expressions and arrow functions'
      ],
      correctIndex: 0,
      explanation: 'Function declarations are hoisted entirely; function expressions and arrow functions are hoisted as variables only.'
    },
    {
      question: 'What is the output of console.log(hoisted()); function hoisted() { return "Hello"; }',
      choices: ['"Hello"', 'undefined', 'Error', 'null'],
      correctIndex: 0,
      explanation: 'The function declaration hoisted is fully hoisted, so calling it before its definition works and returns "Hello".'
    },
    {
      question: 'What is the output of console.log(x); let x = 5;',
      choices: ['5', 'undefined', 'null', 'ReferenceError'],
      correctIndex: 3,
      explanation: 'Accessing a let variable before declaration triggers a ReferenceError due to the temporal dead zone.'
    },
    {
      question: 'Which method checks if an array includes a certain value?',
      choices: ['arr.contains(value)', 'arr.has(value)', 'arr.includes(value)', 'arr.exists(value)'],
      correctIndex: 2,
      explanation: 'includes returns true if the array contains the specified value.'
    },
    {
      question: 'Which statement about const is true?',
      choices: [
        'const variables cannot be reassigned or mutated at all',
        'const prevents reassigning the binding but object contents can still change',
        'const makes values deeply immutable',
        'const is function scoped'
      ],
      correctIndex: 1,
      explanation: 'const prevents rebinding the variable, but if it refers to an object or array, its contents can still be mutated.'
    },
    {
      question: 'What is the output of const obj = { a: 1 }; obj.a = 2; console.log(obj.a);',
      choices: ['1', '2', 'Error', 'undefined'],
      correctIndex: 1,
      explanation: 'The reference obj is constant but the object is mutable, so its a property can change to 2.'
    },
    {
      question: 'What is a template literal?',
      choices: [
        'A precompiled string',
        'A string defined with backticks that can include expressions with ${ }',
        'A JSON template',
        'A string used only for HTML'
      ],
      correctIndex: 1,
      explanation: 'Template literals use backticks and support interpolation with ${expression} and multiline strings.'
    },
    {
      question: 'Which is a valid template literal?',
      choices: [
        '"Hello ${name}"',
        '\'Hello ${name}\'',
        '`Hello ${name}`',
        'Hello ${name}'
      ],
      correctIndex: 2,
      explanation: 'Interpolation works inside backtick strings only, so `Hello ${name}` is the valid template literal.'
    },
    {
      question: 'How do you export a named function in ES modules?',
      choices: [
        'module.export function myFunc() {}',
        'export function myFunc() {}',
        'exports.myFunc = function() {} in browser modules',
        'export: myFunc()'
      ],
      correctIndex: 1,
      explanation: 'Named exports use the export keyword before declarations, for example export function myFunc() {}.'
    },
    {
      question: 'How do you import a named function myFunc from ./utils.js?',
      choices: [
        'import { myFunc } from "./utils.js";',
        'require("./utils.js").myFunc; in browser modules',
        'import myFunc from "./utils.js"; only',
        'include myFunc from "./utils.js";'
      ],
      correctIndex: 0,
      explanation: 'Named imports use curly braces: import { myFunc } from "./utils.js";'
    },
    {
      question: 'What is the default export import syntax?',
      choices: [
        'import { default } from "./mod.js";',
        'import * as default from "./mod.js";',
        'import something from "./mod.js";',
        'import default("./mod.js");'
      ],
      correctIndex: 2,
      explanation: 'import something from "./mod.js"; imports the module default export as something.'
    },
    {
      question: 'Which tool checks whether a property exists directly on an object (not its prototype chain)?',
      choices: [
        'in operator',
        'obj.hasOwnProperty("prop")',
        'obj.propertyExists("prop")',
        'Object.exists(obj, "prop")'
      ],
      correctIndex: 1,
      explanation: 'hasOwnProperty checks only own properties of the object, not inherited ones.'
    },
    {
      question: 'What is the result of const a = { x: 1 }; const b = a; b.x = 2; console.log(a.x);',
      choices: ['1', '2', 'undefined', 'Error'],
      correctIndex: 1,
      explanation: 'a and b refer to the same object, so changing b.x also changes a.x.'
    },
    {
      question: 'How do you make a shallow copy of an object obj?',
      choices: [
        'const copy = obj;',
        'const copy = Object.copy(obj);',
        'const copy = { ...obj };',
        'const copy = new obj();'
      ],
      correctIndex: 2,
      explanation: 'The spread operator { ...obj } copies own enumerable properties into a new object (shallow copy).'
    },
    {
      question: 'What is event delegation?',
      choices: [
        'Assigning one event to multiple elements at once',
        'Attaching a single event listener to a parent element to handle events from its children',
        'Delegating events from browser to server',
        'Combining multiple events into one'
      ],
      correctIndex: 1,
      explanation: 'Event delegation uses event bubbling so a single listener on a parent can handle events from many child elements.'
    },
    {
      question: 'Which built-in data structure maintains insertion order and uses key value pairs with keys of any type?',
      choices: ['Object', 'Map', 'Set', 'Array'],
      correctIndex: 1,
      explanation: 'Map allows keys of any type and preserves insertion order.'
    },
    {
      question: 'Which structure holds unique values only (no duplicates)?',
      choices: ['Array', 'Map', 'Set', 'Object'],
      correctIndex: 2,
      explanation: 'Set stores unique values; adding the same value again has no effect.'
    },
    {
      question: 'What does "use strict" mainly help with?',
      choices: [
        'Faster network requests',
        'Cleaner syntax highlighting',
        'Catching common mistakes and unsafe actions',
        'Making code run in parallel'
      ],
      correctIndex: 2,
      explanation: 'Strict mode throws errors for unsafe actions such as implicit globals and disallows some problematic behavior.'
    },
    {
      question: 'What does Symbol() create?',
      choices: [
        'A unique immutable value usable as an object key',
        'A string alias',
        'A new numeric data type',
        'A private variable that cannot be accessed'
      ],
      correctIndex: 0,
      explanation: 'Symbols are unique, immutable primitives often used as object property keys.'
    },
    {
      question: 'What is the main difference between == and ===?',
      choices: [
        '=== compares only types',
        '== compares only values',
        '=== compares value and type without coercion',
        'They are identical'
      ],
      correctIndex: 2,
      explanation: '=== performs strict equality (no coercion) while == allows type coercion.'
    },
    {
      question: 'What does Object.freeze(obj) do?',
      choices: [
        'Prevents adding, removing, or changing properties of obj',
        'Prevents only adding new properties',
        'Prevents only deleting properties',
        'Makes deep immutable copies of nested objects'
      ],
      correctIndex: 0,
      explanation: 'A frozen object cannot have its existing properties changed, added, or removed (shallow freeze).'
    },
    {
      question: 'What is the output of console.log(typeof [].constructor)?',
      choices: ['"array"', '"object"', '"function"', '"constructor"'],
      correctIndex: 2,
      explanation: '[].constructor is Array, which is a function, so typeof returns "function".'
    },
    {
      question: 'What does Object.keys(obj) return?',
      choices: [
        'All values of obj',
        'An array of obj own enumerable property names',
        'Prototype chain keys',
        'A Map of key value entries'
      ],
      correctIndex: 1,
      explanation: 'Object.keys returns an array of own enumerable property names.'
    },
    {
      question: 'What is the main difference between for...in and for...of?',
      choices: [
        'for...in iterates values; for...of keys',
        'for...in iterates keys; for...of iterates values of iterables',
        'They are identical',
        'for...of only works on plain objects'
      ],
      correctIndex: 1,
      explanation: 'for...in loops over enumerable property names, while for...of iterates values of iterables like arrays and strings.'
    },
    {
      question: 'What will console.log("2" * "3") log?',
      choices: ['"23"', '6', 'NaN', '"6"'],
      correctIndex: 1,
      explanation: 'The * operator coerces both strings to numbers, so "2" * "3" becomes 2 * 3 which is 6.'
    },
    {
      question: 'What is the output of console.log(Boolean("false"))?',
      choices: ['true', 'false', 'NaN', 'Error'],
      correctIndex: 0,
      explanation: 'Any non empty string is truthy, so Boolean("false") is true.'
    },
    {
      question: 'What is function currying?',
      choices: [
        'Combining two functions into one',
        'Transforming a multi argument function into a series of functions each taking one argument',
        'Removing parameters from a function',
        'Overriding built in functions'
      ],
      correctIndex: 1,
      explanation: 'Currying transforms a function f(a, b, c) into f(a)(b)(c), enabling partial application.'
    },
    {
      question: 'What is the main purpose of JavaScript in web development?',
      choices: [
        'Styling web pages',
        'Structuring content',
        'Adding interactivity and dynamic behavior',
        'Serving web pages from the server'
      ],
      correctIndex: 2,
      explanation: 'HTML structures content, CSS styles it, and JavaScript adds logic, interactivity, and dynamic behavior.'
    }
  ];

  QUESTIONS.forEach(function(q, index) {
    const item = form.addMultipleChoiceItem();
    item.setTitle((index + 1) + '. ' + q.question)
        .setPoints(1);

    const choices = q.choices.map(function(choice, i) {
      return item.createChoice(choice, i === q.correctIndex);
    });

    item.setChoices(choices);

    const correctFb = FormApp.createFeedback()
      .setText('Correct! ' + q.explanation)
      .build();
    const incorrectFb = FormApp.createFeedback()
      .setText('Not quite. ' + q.explanation)
      .build();

    item.setFeedbackForCorrect(correctFb);
    item.setFeedbackForIncorrect(incorrectFb);
  });

  Logger.log('Edit URL: ' + form.getEditUrl());
  Logger.log('Live URL: ' + form.getPublishedUrl());
}